Как я могу программно найти частоту процессора с C
Я пытаюсь выяснить, есть ли хоть какое-то представление о частоте ЦП системы, на которой работает мой C-код.
Чтобы уточнить, я ищу абстрактное решение (которое не будет привязано к конкретной архитектуре или ОС), которое может дать мне представление о частоте работы компьютера, на котором выполняется мой код. Мне не нужно быть точным, но я бы хотел быть в центре событий (т. Е. У меня есть процессор с частотой 2,2 ГГц, я бы хотел сказать в своей программе, что я в пределах нескольких сотен МГц этого)
У кого-нибудь есть идея использовать стандартный C-код?
5 ответов
То, как вы находите частоту ЦП, зависит как от архитектуры, так и от ОС, и абстрактного решения не существует.
Если бы мы были более 20 лет назад, и вы использовали ОС без переключения контекста, а процессор выполнял инструкции, приведенные по порядку, вы могли бы написать некоторый код C в цикле и рассчитать его время, а затем на основе сборки, в которую он был скомпилирован. вычислить количество инструкций во время выполнения. Это уже делает предположение, что каждая инструкция занимает 1 такт, что является довольно плохим предположением с тех пор, как процессоры конвейеризовались.
Но любая современная ОС будет переключаться между несколькими процессами. Даже тогда вы можете попытаться рассчитать кучу одинаковых for
цикл запускается (игнорируя время, необходимое для сбоев страниц и множество других причин, по которым ваш процессор может зависнуть) и получает среднее значение.
И даже если предыдущее решение работает, у вас есть многозадачные процессоры. С любым современным процессором можно переупорядочить ваши инструкции, выпустить их несколько за один такт или даже распределить их по ядрам.
Для полноты картины уже существует простое, быстрое, точное решение в пользовательском режиме с огромным недостатком: оно работает только на процессорах Intel Skylake, Kabylake и более новых. Точное требование - поддержка уровня 16h CPUID. В соответствии с Руководством разработчика программного обеспечения Intel 325462, выпуск 59, стр. 770:
CPUID.16h.EAX = базовая частота процессора (в МГц);
CPUID.16h.EBX = максимальная частота (в МГц);
CPUID.16h.ECX = частота шины (эталонная) (в МГц).
Пример кода Visual Studio 2015:
#include <stdio.h>
#include <intrin.h>
int main(void) {
int cpuInfo[4] = { 0, 0, 0, 0 };
__cpuid(cpuInfo, 0);
if (cpuInfo[0] >= 0x16) {
__cpuid(cpuInfo, 0x16);
//Example 1
//Intel Core i7-6700K Skylake-H/S Family 6 model 94 (506E3)
//cpuInfo[0] = 0x00000FA0; //= 4000 MHz
//cpuInfo[1] = 0x00001068; //= 4200 MHz
//cpuInfo[2] = 0x00000064; //= 100 MHz
//Example 2
//Intel Core m3-6Y30 Skylake-U/Y Family 6 model 78 (406E3)
//cpuInfo[0] = 0x000005DC; //= 1500 MHz
//cpuInfo[1] = 0x00000898; //= 2200 MHz
//cpuInfo[2] = 0x00000064; //= 100 MHz
//Example 3
//Intel Core i5-7200 Kabylake-U/Y Family 6 model 142 (806E9)
//cpuInfo[0] = 0x00000A8C; //= 2700 MHz
//cpuInfo[1] = 0x00000C1C; //= 3100 MHz
//cpuInfo[2] = 0x00000064; //= 100 MHz
printf("EAX: 0x%08x EBX: 0x%08x ECX: %08x\r\n", cpuInfo[0], cpuInfo[1], cpuInfo[2]);
printf("Processor Base Frequency: %04d MHz\r\n", cpuInfo[0]);
printf("Maximum Frequency: %04d MHz\r\n", cpuInfo[1]);
printf("Bus (Reference) Frequency: %04d MHz\r\n", cpuInfo[2]);
} else {
printf("CPUID level 16h unsupported\r\n");
}
return 0;
}
Можно найти общее решение, которое правильно определяет рабочую частоту для одного или нескольких потоков. Это не требует прав администратора / root или доступа к конкретным модельным регистрам. Я проверил это на Linux и Windows на процессорах Intel, включая Nahalem, Ivy Bridge и Haswell с одним сокетом до четырех сокетов (40 потоков). Все результаты отклоняются менее чем на 0,5% от правильных ответов. Прежде чем я покажу вам, как это сделать, позвольте мне показать результаты (из GCC 4.9 и MSVC2013):
Linux: E5-1620 (Ivy Bridge) @ 3.60GHz
1 thread: 3.789, 4 threads: 3.689 GHz: (3.8-3.789)/3.8 = 0.3%, 3.7-3.689)/3.7 = 0.3%
Windows: E5-1620 (Ivy Bridge) @ 3.60GHz
1 thread: 3.792, 4 threads: 3.692 GHz: (3.8-3.789)/3.8 = 0.2%, (3.7-3.689)/3.7 = 0.2%
Linux: 4xE7-4850 (Nahalem) @ 2.00GHz
1 thread: 2.390, 40 threads: 2.125 GHz:, (2.4-2.390)/2.4 = 0.4%, (2.133-2.125)/2.133 = 0.4%
Linux: i5-4250U (Haswell) CPU @ 1.30GHz
1 thread: within 0.5% of 2.6 GHz, 2 threads wthin 0.5% of 2.3 GHz
Windows: 2xE5-2667 v2 (Ivy Bridge) @ 3.3 GHz
1 thread: 4.000 GHz, 16 threads: 3.601 GHz: (4.0-4.0)/4.0 = 0.0%, (3.6-3.601)/3.6 = 0.0%
Я получил идею по этой ссылке http://randomascii.wordpress.com/2013/08/06/defective-heat-sinks-causing-garbage-gaming/
Для этого вы сначала делаете то, что делаете 20 лет назад. Вы пишете некоторый код с циклом, в котором вы знаете время ожидания и время его. Вот что я использовал:
static int inline SpinALot(int spinCount)
{
__m128 x = _mm_setzero_ps();
for(int i=0; i<spinCount; i++) {
x = _mm_add_ps(x,_mm_set1_ps(1.0f));
}
return _mm_cvt_ss2si(x);
}
Это имеет переносимую петлевую зависимость, поэтому ЦП не может переупорядочить это, чтобы уменьшить задержку. Это всегда занимает 3 такта на одну итерацию. ОС не будет переносить поток в другое ядро, потому что мы будем связывать потоки.
Затем вы запускаете эту функцию на каждом физическом ядре. Я сделал это с OpenMP. Потоки должны быть связаны для этого. В Linux с GCC вы можете использовать export OMP_PROC_BIND=true
связать темы и предполагая, что у вас есть ncores
физическое ядро также export OMP_NUM_THREADS=ncores
, Если вы хотите программно связать и найти количество физических ядер для процессоров Intel, посмотрите это с помощью программно-определяемого числа-физических-процессоров-ядер-или-если-гипер-потоков и потоков-сходства-с-windows-msvc- и-openmp.
void sample_frequency(const int nsamples, const int n, float *max, int nthreads) {
*max = 0;
volatile int x = 0;
double min_time = DBL_MAX;
#pragma omp parallel reduction(+:x) num_threads(nthreads)
{
double dtime, min_time_private = DBL_MAX;
for(int i=0; i<nsamples; i++) {
#pragma omp barrier
dtime = omp_get_wtime();
x += SpinALot(n);
dtime = omp_get_wtime() - dtime;
if(dtime<min_time_private) min_time_private = dtime;
}
#pragma omp critical
{
if(min_time_private<min_time) min_time = min_time_private;
}
}
*max = 3.0f*n/min_time*1E-9f;
}
Наконец запустите сэмплер в цикле и распечатайте результаты
int main(void) {
int ncores = getNumCores();
printf("num_threads %d, num_cores %d\n", omp_get_max_threads(), ncores);
while(1) {
float max1, median1, max2, median2;
sample_frequency(1000, 1000000, &max2, &median2, ncores);
sample_frequency(1000, 1000000, &max1, &median1,1);
printf("1 thread: %.3f, %d threads: %.3f GHz\n" ,max1, ncores, max2);
}
}
Я не проверял это на процессорах AMD. Я думаю, что процессоры AMD с модулями (например, Bulldozer) должны будут связываться с каждым модулем, а не с каждым "ядром" AMD. Это может быть сделано с export GOMP_CPU_AFFINITY
с GCC. Вы можете найти полный рабочий пример по адресу https://bitbucket.org/zboson/frequency который работает в Windows и Linux на процессорах Intel и правильно найдет количество физических ядер для процессоров Intel (по крайней мере, начиная с Nahalem) и связывает их с каждое физическое ядро (без использования OMP_PROC_BIND
который MSVC не имеет).
Частота ЦП связана с аппаратным обеспечением, поэтому нет никакого общего метода, который вы можете применить, чтобы получить его, она также зависит от используемой ОС.
Например, если вы используете Linux, вы можете либо прочитать файл / proc / cpuinfo, либо проанализировать загрузочный журнал dmesg, чтобы получить это значение, либо, если вы хотите, вы можете посмотреть, как ядро linux обрабатывает этот материал, здесь и попытаться настроить код чтобы удовлетворить ваши потребности:
https://github.com/torvalds/linux/blob/master/arch/x86/kernel/cpu/proc.c
С уважением.
Я полагаю, что один из способов получить тактовую частоту от программного обеспечения - это знание кода Справочного руководства по аппаратному обеспечению (HRM) в программном обеспечении. Вы можете прочитать регистры конфигурации часов из программного обеспечения. Предполагая, что вы знаете исходную тактовую частоту, программное обеспечение может использовать значения умножителя и делителя из тактовых регистров и применять соответствующие формулы, как указано в HRM, для получения тактовой частоты.