Определение тактовой частоты процессора (на ядро, на процессор) - PullRequest
34 голосов
/ 02 декабря 2011

Такие программы, как CPUz, очень хорошо дают подробную информацию о системе (скорость шины, время памяти и т. Д.)

Однако существует ли программный способ вычисления частоты на ядро ​​(и на процессор, в многопроцессорных системах с несколькими ядрами на процессор) без необходимости иметь дело с информацией о конкретном процессоре.

Я пытаюсь разработать средство защиты от мошенничества (для использования с соревнованиями по тестированию с ограничением по тактовой частоте), которое сможет записывать тактовую частоту ЦП во время теста производительности для всех активных ядер в системе (для всех процессоров.)

Ответы [ 6 ]

30 голосов
/ 02 декабря 2011

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

То, что вы пытаетесь сделать, очень сложно - до такой степени, что это непрактично по следующим причинам:

  • Нет портативного способа получить частоту процессора. rdtsc делает НЕ всегда дает правильную частоту из-за таких эффектов, как SpeedStep и Turbo Boost.
  • Все известные методы измерения частоты требуют точного измерения времени. Однако определенный мошенник может вмешаться во все часы и таймеры в системе.
  • Точное считывание частоты процессора, а также времени защищенным способом потребует доступа на уровне ядра. Это подразумевает подписание драйвера для Windows.

Нет портативного способа получить частоту процессора:

«Простой» способ получить частоту ЦП - это вызвать rdtsc дважды с фиксированной временной продолжительностью между ними. Тогда деление разницы даст вам частоту.

Проблема в том, что rdtsc не дает истинную частоту процессора. Поскольку приложения реального времени, такие как игры, полагаются на это, rdtsc должно быть согласованным благодаря регулированию ЦП и Turbo Boost. Поэтому, как только ваша система загрузится, rdtsc будет всегда работать с той же скоростью (если только вы не начнете связываться со скоростями шины с SetFSB или чем-то еще).

Например, на моем Core i7 2600K rdtsc всегда будет показывать частоту на 3.4 GHz. Но на самом деле он работает на холостом ходу в 1.6 GHz и работает до 4.6 GHz под нагрузкой через разогнанный множитель Turbo Boost в 46x.

Но как только вы найдете способ измерить истинную частоту (или вы достаточно довольны rdtsc), вы можете легко получить частоту каждого ядра, используя thread-affinities .

Получение истинной частоты:

Чтобы получить реальную частоту процессора, вам необходимо получить доступ либо к MSR (регистрам для конкретной модели), либо к счетчикам производительности оборудования.

Это инструкции уровня ядра, поэтому они требуют использования драйвера. Если вы пытаетесь сделать это в Windows с целью распространения, вам нужно будет пройти через надлежащий протокол подписи драйверов. Кроме того, код будет отличаться в зависимости от марки и модели процессора, поэтому для каждого поколения процессоров вам потребуется различный код обнаружения.

Как только вы дойдете до этой стадии, есть множество способов прочитать частоту.

На процессорах Intel аппаратные счетчики позволяют считать необработанные циклы ЦП. В сочетании с методом точного измерения реального времени (следующий раздел) вы можете вычислить истинную частоту. MSR предоставляют вам доступ к другой информации, такой как множитель частоты процессора.


Все известные методы измерения частоты требуют точного измерения времени:

Возможно, это большая проблема. Вам нужен таймер, чтобы можно было измерить частоту. Способный хакер сможет взломать все часы, которые вы можете использовать в C / C ++. Это включает в себя все следующее:

  • clock()
  • gettimeofday()
  • QueryPerformanceCounter()
  • и т.д ...

Список можно продолжать и продолжать. Другими словами, вы не можете доверять ни одному из таймеров, поскольку способный хакер сможет подделать их все. Например, clock() и gettimeofday() могут быть обмануты путем изменения системных часов непосредственно в ОС. Дурачиться QueryPerformanceCounter() сложнее.

Получение точного измерения времени:

Все часы, перечисленные выше, уязвимы, потому что они часто так или иначе получены из одних и тех же системных часов. И эти системные базовые часы часто привязаны к системным базовым часам, которые можно изменить после того, как система уже загружена с помощью утилит разгона.

Таким образом, единственный способ получить надежное и защищенное от времени измерение времени - это прочитать внешние часы, такие как HPET или ACPI . К сожалению, для этого также требуется доступ на уровне ядра.


Подведем итог:

Создание любого типа защищенного от взлома теста почти наверняка потребует написания драйвера режима ядра, который требует подписи сертификата для Windows. Это часто является слишком обременительным для случайных авторов тестов.

Это привело к нехватке защищенных от взломов эталонных тестов, что, вероятно, способствовало общему снижению конкурентоспособного сообщества разгона в последние годы.

3 голосов
/ 08 августа 2015

Я понимаю, что на это уже ответили.Я также понимаю, что это в основном черное искусство, поэтому, пожалуйста, примите это или оставьте - или предложите обратную связь.

В поисках определения тактовой частоты на дросселированных (спасибо microsft, hp и dell) хостах HyperV (ненадежный счетчик перфектов) и гости HyperV (могут получить только базовую частоту процессора, а не текущую), мне удалось, благодаря пробной ошибке и случайности, создать цикл, который повторяется ровно один раз за такт.

Код выглядит следующим образом - C # 5.0, SharpDev, 32 бита, Target 3.5, Оптимизация включена (крайне важно), нет активного отладчика (важно)

        long frequency, start, stop;
        double multiplier = 1000 * 1000 * 1000;//nano
        if (Win32.QueryPerformanceFrequency(out frequency) == false)
            throw new Win32Exception();

        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1);
        const int gigahertz= 1000*1000*1000;
        const int known_instructions_per_loop = 1; 

        int iterations = int.MaxValue;
        int g = 0;

        Win32.QueryPerformanceCounter(out start);
        for( i = 0; i < iterations; i++)
        {
            g++;
            g++;
            g++;
            g++;
        }
        Win32.QueryPerformanceCounter(out stop);

        //normal ticks differs from the WMI data, i.e 3125, when WMI 3201, and CPUZ 3199
        var normal_ticks_per_second = frequency * 1000;
        var ticks = (double)(stop - start);
        var time = (ticks * multiplier) /frequency;
        var loops_per_sec = iterations / (time/multiplier);
        var instructions_per_loop = normal_ticks_per_second  / loops_per_sec;

        var ratio = (instructions_per_loop / known_instructions_per_loop);
        var actual_freq = normal_ticks_per_second / ratio;

        Console.WriteLine( String.Format("Perf counhter freq: {0:n}", normal_ticks_per_second));
        Console.WriteLine( String.Format("Loops per sec:      {0:n}", loops_per_sec));
        Console.WriteLine( String.Format("Perf counter freq div loops per sec: {0:n}", instructions_per_loop));
        Console.WriteLine( String.Format("Presumed freq: {0:n}", actual_freq));
        Console.WriteLine( String.Format("ratio: {0:n}", ratio));

Примечания

  • 25 инструкций за цикл, если отладчик активен
  • Попробуйте запустить цикл 2 или 3 секунды перед рукой, чтобы ускорить процессор (или, по крайней мере, попытаться увеличить скорость, зная, насколько сильно работают серверы в эти дни)
  • Протестировано на 64-битном Core2 и Haswell Pentium и сравнено с CPU-Z
2 голосов
/ 13 декабря 2011

Я ранее писал на эту тему (вместе с базовым алгоритмом): здесь . Насколько мне известно, алгоритм (см. Обсуждение) очень точный. Например, Windows 7 сообщает, что мои тактовые частоты процессора равны 2,00 ГГц, CPU-Z - 1994-1996 МГц, а мой алгоритм - 1995025-1995075 кГц.

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

Дополнительная информация здесь и здесь .

Что касается регулирования скорости, то я действительно не вижу в этом проблемы, если только приложение не использует значения скорости для определения истекшего времени и того, что сами времена чрезвычайно важны. Например, если деление требует x тактовых циклов для завершения, не имеет значения, работает ли процессор на частоте 3 ГГц или 300 МГц: ему все равно понадобится x тактовых циклов, и единственное отличие состоит в том, что оно завершит деление в десятой доле. времени на частоте @ 3 ГГц.

1 голос
/ 20 января 2017

Вам необходимо использовать CallNtPowerInformation .Вот пример кода из проекта putil .При этом вы можете получить текущую и максимальную частоту процессора.Насколько я знаю, невозможно получить частоту для каждого процессора.

1 голос
/ 10 октября 2016

Следует обратиться к этому техническому документу: Технология Intel® Turbo Boost в процессорах на базе микроархитектуры Intel® Core ™ (Nehalem) .По сути, производим несколько считываний счетчика фиксированной производительности UCC за период выборки T.

Relative.Freq = Delta(UCC)  / T

Where:
   Delta() = UCC @ period T
                 - UCC @ period T-1

Начиная с архитектуры Nehalem, UCC увеличивает и уменьшает количество тактов щелчка по отношению к состоянию Unhalted ядра.

Когда SpeedStep или Turbo Boost активированы, расчетная частота с использованием UCC будет измеряться соответственно;в то время как TSC остается постоянным.Например, Turbo Boost в действии показывает, что Delta (UCC) больше или равна Delta (TSC)

Пример в функции Core_Cycle function at Cyring |CoreFreq GitHub.

1 голос
/ 02 декабря 2011

Один из самых простых способов сделать это - использовать RDTSC, но, учитывая, что это относится к механизмам анти-мошенничества, я бы добавил это в качестве драйвера ядра или резидентного фрагмента кода с гипервизором.

Возможно, вам также понадобится свернуть свой собственный временной код **, что снова можно сделать с помощью RDTSC (QPC, как используется в приведенном ниже примере, использует RDTSC, и его на самом деле очень просто повернуть вспятьспроектировать и использовать локальную копию, что означает вмешательство в нее, вам нужно будет вмешаться в ваш драйвер).

void GetProcessorSpeed()
{
    CPUInfo* pInfo = this;
    LARGE_INTEGER qwWait, qwStart, qwCurrent;
    QueryPerformanceCounter(&qwStart);
    QueryPerformanceFrequency(&qwWait);
    qwWait.QuadPart >>= 5;
    unsigned __int64 Start = __rdtsc();
    do
    {
        QueryPerformanceCounter(&qwCurrent);
    }while(qwCurrent.QuadPart - qwStart.QuadPart < qwWait.QuadPart);
    pInfo->dCPUSpeedMHz = ((__rdtsc() - Start) << 5) / 1000000.0;
}

** Я хотел бы, чтобы это было в целях безопасности, как упоминалось в @Mystical, но какЯ никогда не чувствовал желания подорвать низкоуровневые механизмы системной синхронизации, возможно, они были бы более сложными, было бы неплохо, если бы Mystical мог что-то добавить к этому:)

...