Как я могу программно определить частоту процессора с

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

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

есть ли у кого-нибудь идея использовать стандартный код C?

5 ответов


Как найти частоту процессора архитектуры и зависимые ОС, и нет абстрактных решений.

Если бы мы были 20+ лет назад, и вы использовали ОС без переключения контекста, и процессор выполнил инструкции, данные ему по порядку, вы могли бы написать некоторый код C в цикле и времени, а затем на основе сборки он был скомпилирован в вычислить количество инструкций во время выполнения. Это уже делает предположение, что каждая инструкция занимает 1 такт, который это довольно плохое предположение с тех пор, как конвейерные процессоры.

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

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


возможно найти общее решение которое получает рабочую частоту правильно для одного потока или много потоков. Это не требует прав администратора/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, см. Это программно-определить число физических процессоров-ядер-или-если-многопоточность и thread-affinity-with-windows-msvc-and-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 с модулями (e.g бульдозер) придется привязывать к каждому модулю не каждое "ядро" AMD. Это можно сделать с помощью export GOMP_CPU_AFFINITY С GCC. Вы можете найти полную работу пример в https://bitbucket.org/zboson/frequency который работает на Windows и Linux на процессорах Intel и правильно найдет количество физических ядер для процессоров Intel (по крайней мере, начиная с Nahalem) и привязывает их к каждому физическому ядру (без использования OMP_PROC_BIND которого MSVC не имеет).


для полноты уже существует простое, быстрое, точное решение для пользовательского режима с огромным недостатком: оно работает только на процессорах Intel Skylake, Kabylake и более новых. Точное требование-поддержка уровня CPUID 16h. Согласно руководству разработчика программного обеспечения Intel 325462 release 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;
}

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

например, если вы используете Linux, вы можете прочитать файл /proc / cpuinfo или вы можете разобрать команды dmesg boot log, чтобы получить это значение, или если вы хотите, вы можете увидеть, как ядро linux обрабатывает этот материал здесь и пытается настроить код для удовлетворения ваших потребностей :

https://github.com/torvalds/linux/blob/master/arch/x86/kernel/cpu/proc.c

с уважением.


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