Измерение тактовой частоты процессора Intel в C++


Я создал короткую программу для непосредственного измерения тактовой частоты процессора Intel. Он работает, спит на срок, указанный пользователем, а затем измеряет количество тактов и количество времени, которое прошло, пока он спал, затем делит прошедшее тактов, прошедшее время, чтобы получить частоту процессора.

  • Это ниже код на вещи с точки зрения стиля и читабельности?
  • Есть ли основания стратегии, используемые и описанные ниже не дают точные результаты?

Любая обратная связь приветствуется! Если вы хотите проверить код, вы можете скачать ее с git clone <a href="https://github.com/firetotherain/CPUHertz/tree/f736f4ea3fe476fd55bd6f9bc5b7f2d2d9b4491d" rel="noreferrer">https://github.com/firetotherain/CPUHertz.git</a>

#include <thread>
#include <chrono>
#include <cstdio>

//Uses assembly command to get the current value of the cycle "counter"
uint64_t get_cycles()
{
    unsigned int lo,hi;
    __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
    return ((uint64_t)hi << 32) | lo;
}

//Defines unit of time to measure seconds
typedef std::chrono::duration<double, std::ratio<1,1>> seconds_t;

//In main(int argc, char** argv) this global variable is initialized to argv[0]
const char* program_name;

//Initialized at the start of the program; gets the current time
auto start_time = std::chrono::high_resolution_clock::now();


//Returns the time since the start of the program, measured in seconds
//Has accuracy identical to that of std::chrono::high_resolution_clock
double age() 
{
    return seconds_t(std::chrono::high_resolution_clock::now() - start_time).count();
}

//Prints the program usage
void PrintUsage() 
{
    printf("Usage:\n");
    printf("%s [measurment duration]\n", program_name);
}
int main(int argc, char** argv)
{
    using namespace std::chrono_literals;
    program_name = argv[0];
    int sleeptime = 100;
    switch(argc) {
    case 1:
        sleeptime = 100;
        break;
    case 2:
        try 
        {
            sleeptime = std::stoi(argv[1]);
        } 
        catch(...) 
        {
            printf("Error: argument was not an integer.");
            PrintUsage();
            return 1;
        }
        break;
    default:
        printf("Error: too many arguments.");
        PrintUsage();
        return 1;
        break;
    }
    uint64_t cycles_start = get_cycles();
    double time_start = age();
    std::this_thread::sleep_for(sleeptime * 1ms);
    uint64_t elapsed_cycles = get_cycles() - cycles_start;
    double elapsed_time = age() - time_start;
    printf("CPU MHz: %.3f\n", elapsed_cycles / elapsed_time / 1000000.0);
}


550
10
задан 1 марта 2018 в 12:03 Источник Поделиться
Комментарии
1 ответ

Есть ряд вещей, которые вы можете использовать, чтобы улучшить вашу программу.

Точность


Есть ли основания стратегии, используемые и описанные ниже не дают точные результаты?

Да! Существует множество причин, которые в каждом отдельно перечисленных в пунктах ниже, за более общей информацией о стиле кода и структуры.

Понимаю Режим Управления Системой

Код в настоящее время не учитывает возможности процессора ввода системы управления режим (СММ). Один простой способ сдачи машины в СММ есть он переходит в режим ожидания. Мой ноутбук настроен на переход в режим ожидания при закрытии крышки и вернулся, когда я открываю его снова, поэтому, когда я сделал, что когда запущена эта программа, я получил такой результат:


Процессора МГц: 2365092677585.790

А было бы здорово на самом деле иметь процессор, который быстрее, но мой ноутбук не получится, так что этот результат неверен.

Понимаю sleep_for

Что std::this_thread::sleep_for(delay) Не спать по крайней мере delay но это может быть больше, в зависимости от того, что еще в операционной системе и аппаратной занимаетесь в данный момент. Это означает, что существует вариабельность в результатах программы. Пример одна тысяча запусков программы показано графически ниже.
enter image description here
Хотя в этом случае, большинство результатов были примерно правильное значение 2494.225 МГц, результаты бывают разные. Альтернативный способ сделать это было бы использование аппаратных, а не таймер и запустить код времени как часть ядра с отключенными прерываниями.

Понимаю порядку выполнения и влияния кэша

Вариабельность данной программы в ее нынешнем виде такова, что это не имеет значения, но при попытке сделать точного времени с использованием RDTSC инструкция, важно понимать, что процессор не из-за исполнения Заказа. Это означает, что инструкция до или после RDTSC инструкция на самом деле может быть частью продолжительность мероприятия вы пытаетесь измерить если не будут приняты меры. Что обычно используется в сериализации инструкций , таких как CPUID для устранения таких последствий. Корпорация Intel является полезным документом , который описывает это в деталях и как его применять.

Общие советы

Далее следует более общие советы о стиле кодирования и структуры.

Избегайте глобальных переменных

В program_name и start_time переменные являются глобальными. Это обычно лучше, чтобы явно передавать переменные функции будут нужны, а не через туманные неявные взаимосвязи глобальной переменной. Если они должны быть глобальные переменные (что не так), то сделать их static.

Будьте осторожны с размером предположения

Код в настоящее время имеет следующие три строки:

unsigned int lo,hi;
__asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi));
return ((uint64_t)hi << 32) | lo;

Это не неправильно, но я бы, наверное, написать, что с помощью явного uint32_t размеры для hi и lo чтобы исключить возможность компилятора с 16-разрядной unsigned int.

Изолировать код в функцию

Прямо сейчас, сроки делается main. Я бы, наверное, переписать, что бы содержать код времени в функции:

double approx_CPU_MHz(unsigned sleeptime) {
using namespace std::chrono_literals;
uint64_t cycles_start = get_cycles();
double time_start = age();
std::this_thread::sleep_for(sleeptime * 1ms);
uint64_t elapsed_cycles = get_cycles() - cycles_start;
double elapsed_time = age() - time_start;
return elapsed_cycles / elapsed_time / 1000000.0;
}

Думаю, пользователя

В PrintUsage рутина выглядит так:

void PrintUsage() 
{
printf("Usage:\n");
printf("%s [measurment duration]\n", program_name);
}

Есть три проблемы, которые с точки зрения. Во-первых, это не сказать пользователю, единица измерения длительность измерения. Это секунд? Минут? Микросекунд? Пользователь должен посмотреть на исходный код, чтобы ответить на этот вопрос. Во-вторых, он может разумно толковаться как два аргумента, оба из которых являются необязательными. Поставив знак подчеркивания вместо пробела будет исправить. В-третьих, слово "измерение" является ошибкой.

Использовать iostream

Это не неправильно использовать printf но через iostreamС более C++-как вы можете сэкономить во время выполнения обработки. Для оценки printfкомпьютер должен интерпретировать формат строки во-первых, при использовании << означает, что компилятор уже оценили аргумент типа во время компиляции.

11
ответ дан 1 марта 2018 в 04:03 Источник Поделиться