Câu trả lời mới này sử dụng <chrono>
cơ sở của C ++ 11 . Trong khi có các câu trả lời khác hiển thị cách sử dụng <chrono>
, không câu trả lời nào trong số đó chỉ ra cách sử dụng <chrono>
với RDTSC
cơ sở được đề cập trong một số câu trả lời khác ở đây. Vì vậy, tôi nghĩ tôi sẽ chỉ cách sử dụng RDTSC
với <chrono>
. Thêm vào đó, tôi sẽ chứng minh làm thế nào bạn có thể templatize mã thử nghiệm trên đồng hồ để bạn có thể nhanh chóng chuyển đổi giữa RDTSC
và hệ thống của bạn được xây dựng trong các cơ sở đồng hồ (trong đó có khả năng sẽ được dựa trên clock()
, clock_gettime()
và / hoặcQueryPerformanceCounter
.
Lưu ý rằng RDTSC
hướng dẫn dành riêng cho x86. QueryPerformanceCounter
chỉ dành cho Windows. Và clock_gettime()
chỉ có POSIX. Dưới đây tôi giới thiệu hai đồng hồ mới:std::chrono::high_resolution_clock
và std::chrono::system_clock
, nếu bạn có thể giả sử C ++ 11, hiện là đa nền tảng.
Đầu tiên, đây là cách bạn tạo xung nhịp tương thích với C ++ 11 từ rdtsc
hướng dẫn lắp ráp Intel . Tôi sẽ gọi nó là x::clock
:
#include <chrono>
namespace x
{
struct clock
{
typedef unsigned long long rep;
typedef std::ratio<1, 2'800'000'000> period; // My machine is 2.8 GHz
typedef std::chrono::duration<rep, period> duration;
typedef std::chrono::time_point<clock> time_point;
static const bool is_steady = true;
static time_point now() noexcept
{
unsigned lo, hi;
asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return time_point(duration(static_cast<rep>(hi) << 32 | lo));
}
};
} // x
Tất cả những gì xung nhịp này làm là đếm chu kỳ CPU và lưu trữ nó trong một số nguyên 64-bit không dấu. Bạn có thể cần phải chỉnh sửa cú pháp hợp ngữ cho trình biên dịch của mình. Hoặc trình biên dịch của bạn có thể cung cấp một nội tại mà bạn có thể sử dụng thay thế (ví dụ now() {return __rdtsc();}
:).
Để xây dựng một chiếc đồng hồ, bạn phải cung cấp cho nó đại diện (kiểu lưu trữ). Bạn cũng phải cung cấp chu kỳ đồng hồ, phải là một hằng số thời gian biên dịch, mặc dù máy của bạn có thể thay đổi tốc độ đồng hồ ở các chế độ năng lượng khác nhau. Và từ những thứ đó, bạn có thể dễ dàng xác định khoảng thời gian và thời điểm "gốc" của đồng hồ về các nguyên tắc cơ bản này.
Nếu tất cả những gì bạn muốn làm là xuất ra số lần tích tắc đồng hồ, thì việc bạn đưa ra con số nào cho chu kỳ đồng hồ không thực sự quan trọng. Hằng số này chỉ phát huy tác dụng nếu bạn muốn chuyển đổi số tích tắc đồng hồ thành một số đơn vị thời gian thực chẳng hạn như nano giây. Và trong trường hợp đó, bạn có khả năng cung cấp tốc độ đồng hồ càng chính xác thì việc chuyển đổi thành nano giây, (mili giây, bất kỳ) càng chính xác.
Dưới đây là mã ví dụ hiển thị cách sử dụng x::clock
. Trên thực tế, tôi đã tạo mẫu mã trên đồng hồ để chỉ cách bạn có thể sử dụng nhiều đồng hồ khác nhau với cùng một cú pháp. Thử nghiệm cụ thể này cho thấy chi phí lặp lại khi chạy những gì bạn muốn theo thời gian trong một vòng lặp:
#include <iostream>
template <class clock>
void
test_empty_loop()
{
// Define real time units
typedef std::chrono::duration<unsigned long long, std::pico> picoseconds;
// or:
// typedef std::chrono::nanoseconds nanoseconds;
// Define double-based unit of clock tick
typedef std::chrono::duration<double, typename clock::period> Cycle;
using std::chrono::duration_cast;
const int N = 100000000;
// Do it
auto t0 = clock::now();
for (int j = 0; j < N; ++j)
asm volatile("");
auto t1 = clock::now();
// Get the clock ticks per iteration
auto ticks_per_iter = Cycle(t1-t0)/N;
std::cout << ticks_per_iter.count() << " clock ticks per iteration\n";
// Convert to real time units
std::cout << duration_cast<picoseconds>(ticks_per_iter).count()
<< "ps per iteration\n";
}
Điều đầu tiên mã này làm là tạo đơn vị "thời gian thực" để hiển thị kết quả. Tôi đã chọn pico giây, nhưng bạn có thể chọn bất kỳ đơn vị nào bạn thích, dựa trên tích phân hoặc dấu phẩy động. Ví dụ, có một std::chrono::nanoseconds
đơn vị làm sẵn mà tôi có thể đã sử dụng.
Như một ví dụ khác, tôi muốn in ra số chu kỳ đồng hồ trung bình trên mỗi lần lặp dưới dạng dấu phẩy động, vì vậy tôi tạo một khoảng thời gian khác, dựa trên double, có cùng đơn vị với dấu của đồng hồ (được gọi Cycle
trong mã).
Vòng lặp được tính thời gian với các cuộc gọi đến clock::now()
ở hai bên. Nếu bạn muốn đặt tên cho kiểu được trả về từ hàm này, nó là:
typename clock::time_point t0 = clock::now();
(như được hiển thị rõ ràng trong x::clock
ví dụ và cũng đúng với đồng hồ do hệ thống cung cấp).
Để có được thời lượng về mặt đồng hồ dấu phẩy động, người ta chỉ cần trừ hai điểm thời gian và để nhận giá trị mỗi lần lặp, hãy chia khoảng thời gian đó cho số lần lặp.
Bạn có thể nhận được số lượng trong bất kỳ khoảng thời gian nào bằng cách sử dụng count()
chức năng thành viên. Điều này trả về đại diện bên trong. Cuối cùng, tôi sử dụng std::chrono::duration_cast
để chuyển đổi thời lượng Cycle
thành thời lượng picoseconds
và in nó ra.
Để sử dụng mã này rất đơn giản:
int main()
{
std::cout << "\nUsing rdtsc:\n";
test_empty_loop<x::clock>();
std::cout << "\nUsing std::chrono::high_resolution_clock:\n";
test_empty_loop<std::chrono::high_resolution_clock>();
std::cout << "\nUsing std::chrono::system_clock:\n";
test_empty_loop<std::chrono::system_clock>();
}
Ở trên, tôi thực hiện bài kiểm tra bằng cách sử dụng đồng hồ tự chế của chúng tôi x::clock
và so sánh các kết quả đó với việc sử dụng hai trong số các đồng hồ do hệ thống cung cấp: std::chrono::high_resolution_clock
và std::chrono::system_clock
. Đối với tôi điều này in ra:
Using rdtsc:
1.72632 clock ticks per iteration
616ps per iteration
Using std::chrono::high_resolution_clock:
0.620105 clock ticks per iteration
620ps per iteration
Using std::chrono::system_clock:
0.00062457 clock ticks per iteration
624ps per iteration
Điều này cho thấy rằng mỗi đồng hồ này có khoảng thời gian tích tắc khác nhau, vì số tích tắc trên mỗi lần lặp lại rất khác nhau đối với mỗi đồng hồ. Tuy nhiên, khi chuyển đổi sang một đơn vị thời gian đã biết (ví dụ: pico giây), tôi nhận được kết quả gần giống nhau cho mỗi đồng hồ (số dặm của bạn có thể thay đổi).
Lưu ý rằng mã của tôi hoàn toàn không có "hằng số chuyển đổi ma thuật". Thật vậy, chỉ có hai con số kỳ diệu trong toàn bộ ví dụ:
- Tốc độ đồng hồ của máy tôi để xác định
x::clock
.
- Số lần lặp lại để kiểm tra. Nếu việc thay đổi con số này làm cho kết quả của bạn thay đổi đáng kể, thì bạn có thể nên làm cho số lần lặp lại cao hơn hoặc làm trống máy tính của các quy trình cạnh tranh trong khi thử nghiệm.