Выяснение тактовой частоты процессора (на ядро, на процессор)
Такие программы, как CPUz, очень хорошо дают подробную информацию о системе (скорость шины, время памяти и т. Д.)
Однако существует ли программный способ вычисления частоты на ядро (и на процессор, в многопроцессорных системах с несколькими ядрами на процессор) без необходимости иметь дело с информацией о конкретном процессоре.
Я пытаюсь разработать инструмент защиты от мошенничества (для использования в соревнованиях по тестированию с ограничением по тактовой частоте), который сможет записывать тактовую частоту процессора во время тестовой прогонки для всех активных ядер в системе (для всех процессоров).
6 ответов
Я буду расширять свои комментарии здесь. Это слишком большой и глубокий для меня, чтобы вписаться в комментарии.
То, что вы пытаетесь сделать, очень сложно - до такой степени, что это непрактично по следующим причинам:
- Нет портативного способа получить частоту процессора.
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
), вы можете легко получить частоту каждого ядра, используя привязку потоков.
Получение истинной частоты:
Чтобы получить истинную частоту процессора, вам необходимо получить доступ либо к MSR (регистрам для конкретной модели), либо к счетчикам производительности оборудования.
Это инструкции уровня ядра и, следовательно, требуют использования драйвера. Если вы пытаетесь сделать это в Windows с целью распространения, вам нужно будет пройти через надлежащий протокол подписи драйверов. Кроме того, код будет отличаться в зависимости от марки и модели процессора, поэтому вам потребуется различный код обнаружения для каждого поколения процессоров.
Как только вы дойдете до этой стадии, есть множество способов прочитать частоту.
На процессорах Intel аппаратные счетчики позволяют подсчитывать сырые циклы процессора. В сочетании с методом точного измерения реального времени (следующий раздел) вы можете вычислить истинную частоту. MSR предоставляют вам доступ к другой информации, такой как множитель частоты процессора.
Все известные методы измерения частоты требуют точного измерения времени:
Это, пожалуй, большая проблема. Вам нужен таймер, чтобы можно было измерить частоту. Способный хакер сможет взломать все часы, которые вы можете использовать в C/C++. Это включает в себя все следующее:
clock()
gettimeofday()
QueryPerformanceCounter()
- так далее...
У этого списка нет конца. Другими словами, вы не можете доверять ни одному из таймеров, поскольку способный хакер сможет подделать их все. Например clock()
а также gettimeofday()
может быть обманут, изменяя системные часы непосредственно в ОС. дезориентация QueryPerformanceCounter()
сложнее.
Получение истинного измерения времени:
Все перечисленные выше часы уязвимы, потому что они часто так или иначе получены из одних и тех же системных часов. И эти системные базовые часы часто связаны с системными базовыми часами, которые можно изменить после того, как система уже загружена с помощью утилит разгона.
Таким образом, единственный способ получить надежное и защищенное от времени измерение времени - это считывать внешние часы, такие как HPET или ACPI. К сожалению, для этого также требуется доступ на уровне ядра.
Подвести итоги:
Для создания любого типа защищенного от взлома теста почти наверняка потребуется написание драйвера режима ядра, который требует подписи сертификатов для Windows. Это часто является слишком обременительным для случайных авторов тестов.
Это привело к нехватке защищенных от взломов тестов, что, вероятно, способствовало общему упадку конкурентного сообщества оверклокинга в последние годы.
Я понимаю, что на это уже был дан ответ. Я также понимаю, что это в основном черное искусство, поэтому, пожалуйста, примите это или оставьте - или оставьте отзыв.
В стремлении найти тактовую частоту для дросселированных (благодаря 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
Одним из самых простых способов сделать это является использование 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 мог что-то добавить к этому:)
Ранее я писал на эту тему (вместе с основным алгоритмом): здесь. Насколько мне известно, алгоритм (см. Обсуждение) очень точный. Например, Windows 7 сообщает, что частота моего процессора равна 2,00 ГГц, CPU-Z - 1994-1996 МГц, а мой алгоритм - 1995025-1995075 кГц.
Алгоритм выполняет множество циклов для этого, что приводит к увеличению частоты процессора до максимума (как это будет происходить и во время тестов), поэтому программное обеспечение регулирования скорости не будет задействовано.
Дополнительная информация здесь и здесь.
Что касается регулирования скорости, я действительно не вижу в этом проблемы, если приложение не использует значения скорости для определения истекшего времени и того, что сами времена чрезвычайно важны. Например, если деление требует x тактовых циклов для завершения, не имеет значения, работает ли процессор на частоте 3 ГГц или 300 МГц: ему все равно понадобится x тактовых циклов, и единственное отличие состоит в том, что оно завершит деление за десятую часть. времени на @ 3 ГГц.
Вам нужно использовать CallNtPowerInformation. Вот пример кода из проекта putil. При этом вы можете получить текущую и максимальную частоту процессора. Насколько я знаю, невозможно получить частоту для каждого процессора.
Следует обратиться к этому техническому документу: технология 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
функция в Cyring | CoreFreq GitHub.