Ця нова відповідь використовує засоби C ++ 11 <chrono>
. Хоча існують і інші відповіді , які показують , як використовувати <chrono>
, жоден з них не показує , як використовувати <chrono>
з RDTSC
об'єкта , зазначеного в декількох інших відповідей тут. Так я думав , що показати , як використовувати RDTSC
з <chrono>
. Крім того , я покажу вам , як ви можете templatize коду тестування на годиннику , так що ви можете швидко перемикатися між , RDTSC
і ваша система вбудована в годиннику об'єктів (які, ймовірно , буде заснована на clock()
, clock_gettime()
і / або QueryPerformanceCounter
.
Зауважте, що RDTSC
інструкція є специфічною для x86. QueryPerformanceCounter
є лише Windows. І clock_gettime()
це лише POSIX. Нижче я ввожу два нові годинники: std::chrono::high_resolution_clock
і std::chrono::system_clock
, які, якщо ви можете припустити C ++ 11, тепер є кросплатформенними.
По-перше, ось як ви створюєте годинник, сумісний із C ++ 11, з rdtsc
інструкції щодо складання Intel . Я назву це 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
Все, що робить цей годинник, - це підрахунок циклів процесора та збереження їх у непідписане 64-бітове ціле число. Вам може знадобитися налаштувати синтаксис мови збірки для вашого компілятора. Або ваш компілятор може запропонувати невід'ємний текст, який ви можете використовувати замість цього (наприклад now() {return __rdtsc();}
).
Щоб побудувати годинник, ви повинні надати йому подання (тип зберігання). Ви також повинні надати часовий період, який повинен бути константами часу компіляції, навіть якщо ваша машина може змінювати тактову частоту в різних режимах живлення. І з тих, що ви можете легко визначити "рідну" тривалість часу та часовий термін з точки зору цих основ.
Якщо все, що ви хочете зробити, це вивести кількість тактових годин, це насправді не має значення, яке число ви даєте за годинний період. Ця константа вступає в дію лише у тому випадку, якщо ви хочете перетворити кількість тактових годин в певну одиницю реального часу, наприклад наносекунд. І в цьому випадку, чим точніше ви зможете подати тактову частоту, тим точнішим буде перетворення в наносекунди (мілісекунди, що завгодно).
Нижче наведено приклад коду, який показує, як користуватися x::clock
. Насправді я шаблонував код на годиннику, як хотів би показати, як можна використовувати безліч різних годин із точно таким же синтаксисом. Цей конкретний тест показує, що таке циклічні накладні витрати під час запуску того, що ви хочете провести під циклом:
#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";
}
Перше, що робить цей код - створити одиницю "в реальному часі" для відображення результатів. Я вибрав пікосекунди, але ви можете вибрати будь-які вподобані вам одиниці, як цілі, так і плаваючі точки. Як приклад є попередньо виготовлений std::chrono::nanoseconds
блок, який я міг би використати.
В якості іншого прикладу я хочу роздрукувати середню кількість тактових циклів за ітерацію як плаваючу крапку, тому я створюю іншу тривалість, засновану на подвійному, який має ті самі одиниці, що і галочка годинника (називається Cycle
в коді).
Цикл приурочений до дзвінків clock::now()
з обох сторін. Якщо ви хочете назвати тип, який повертається з цієї функції, це:
typename clock::time_point t0 = clock::now();
(як чітко показано в x::clock
прикладі, і це стосується також системних годин).
Щоб отримати тривалість у плані тактових знаків з плаваючою точкою, потрібно лише відняти дві часові точки, а щоб отримати значення ітерації, поділити цю тривалість на кількість ітерацій.
Ви можете отримати підрахунок за будь-яку тривалість за допомогою функції count()
члена. Це повертає внутрішнє представлення. Нарешті, я використовую std::chrono::duration_cast
для перетворення тривалості Cycle
у тривалість picoseconds
та роздруковую її.
Користуватися цим кодом просто:
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>();
}
Вище я здійснюю тест, використовуючи наші домашні вироби x::clock
, і порівнюю ці результати з використанням двох годинників, що постачаються до системи: std::chrono::high_resolution_clock
і std::chrono::system_clock
. Для мене це виводить:
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
Це свідчить про те, що кожен з цих годин має різний період кліща, оскільки кліщі за ітерацію сильно відрізняються для кожного годинника. Однак при перетворенні на відому одиницю часу (наприклад, пікосекунди) я отримую приблизно однаковий результат для кожного годинника (ваш пробіг може змінюватися).
Зверніть увагу, як мій код повністю не містить "магічних констант конверсії". Дійсно, у всьому прикладі є лише два магічні числа:
- Тактова частота моєї машини для визначення
x::clock
.
- Кількість повторень для перевірки. Якщо зміна цього числа призводить до того, що ваші результати сильно різняться, вам, мабуть, слід збільшити кількість повторень або спорожнити комп'ютер конкуруючих процесів під час тестування.