рдцк, слишком много циклов - PullRequest
8 голосов
/ 30 ноября 2011
#include <stdio.h>
static inline unsigned long long tick() 
{
        unsigned long long d;
        __asm__ __volatile__ ("rdtsc" : "=A" (d) );
        return d;
}

int main()
{
        long long res;
        res=tick();

        res=tick()-res;
        printf("%d",res);
        return 0;
}

Я скомпилировал этот код с помощью gcc с оптимизацией -O0 -O1 -O2 -O3. И я всегда получаю 2000-2500 циклов. Кто-нибудь может объяснить причину этого вывода? Как провести эти циклы?

Первая функция "галочка" неверна. Это правильно .

Еще одна версия функции "галочка"

static __inline__ unsigned long long tick()
{
  unsigned hi, lo;
  __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
  return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

Это код сборки для -O3

 .file  "rdtsc.c"
.section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "%d"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
    leal    4(%esp), %ecx
    andl    $-16, %esp
    pushl   -4(%ecx)
    pushl   %ebp
    movl    %esp, %ebp
    subl    $40, %esp
    movl    %ecx, -16(%ebp)
    movl    %ebx, -12(%ebp)
    movl    %esi, -8(%ebp)
    movl    %edi, -4(%ebp)
#APP
# 6 "rdtsc.c" 1
    rdtsc
# 0 "" 2
#NO_APP
    movl    %edx, %edi
    movl    %eax, %esi
#APP
# 6 "rdtsc.c" 1
    rdtsc
# 0 "" 2
#NO_APP
    movl    %eax, %ecx
    movl    %edx, %ebx
    subl    %esi, %ecx
    sbbl    %edi, %ebx
    movl    %ecx, 4(%esp)
    movl    %ebx, 8(%esp)
    movl    $.LC0, (%esp)
    call    printf
    movl    -16(%ebp), %ecx
    xorl    %eax, %eax
    movl    -12(%ebp), %ebx
    movl    -8(%ebp), %esi
    movl    -4(%ebp), %edi
    movl    %ebp, %esp
    popl    %ebp
    leal    -4(%ecx), %esp
    ret
    .size   main, .-main
    .ident  "GCC: (Debian 4.3.2-1.1) 4.3.2"
    .section    .note.GNU-stack,"",@progbits

Это процессор

processor   : 0
vendor_id   : GenuineIntel
cpu family  : 15
model       : 4
model name  : Intel(R) Xeon(TM) CPU 3.00GHz
stepping    : 3
cpu MHz     : 3000.105
cache size  : 2048 KB
fdiv_bug    : no
hlt_bug     : no
f00f_bug    : no
coma_bug    : no
fpu     : yes
fpu_exception   : yes
cpuid level : 5
wp      : yes
flags       : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss constant_tsc up pebs bts pni
bogomips    : 6036.62
clflush size    : 64

Ответы [ 5 ]

10 голосов
/ 30 ноября 2011

Я пробовал ваш код на нескольких дистрибутивах Linux, работающих на разных процессорах Intel (правда, все более поздних, чем Pentium 4 HT 630, который вы используете). Во всех этих тестах я получил значения от 25 до 50 циклов.

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

7 голосов
/ 30 ноября 2011

Существует большое количество причин для получения большого числа:

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

Обратите внимание, что rdtsc не особенно надежен для определения временибез работы, потому что:

  • Скорость процессора может изменяться, и, следовательно, изменяется длина цикла (при измерении в секундах).
  • Различные процессоры могут иметь разные значения для TSCна данный момент времени.

В большинстве операционных систем используются высокоточные часы или метод синхронизации.clock_gettime в Linux, например, в частности, монотонные часы.(Также следует понимать разницу между настенными часами и монотонными часами: настенные часы могут двигаться назад - даже в UTC.) Думаю, для Windows рекомендуется QueryHighPerformanceCounter.Как правило, эти часы обеспечивают более чем достаточную точность для большинства нужд.


Кроме того, если посмотреть на сборку, похоже, что вы получаете только 32-битный ответ: я не вижу %edx сохраняясь после rdtsc.


Запустив ваш код, я получаю время от 120-150 нс для clock_gettime, используя CLOCK_MONOTONIC, и 70-90 циклов для rdtsc (~ 20 нс прина полной скорости, но я подозреваю, что процессор отключен, и это действительно около 50 нс).(На ноутбуке настольном компьютере (черт возьми, SSH, забыл, на каком компьютере я работал!), Который использует примерно 20% ресурсов процессора) Уверен, что ваша машина не зависает?

4 голосов
/ 30 ноября 2011

Похоже, ваша ОС отключила выполнение RDTSC в пространстве пользователя. И ваше приложение должно переключиться на ядро ​​и обратно, что занимает много циклов.

Это из Руководства разработчика программного обеспечения Intel:

В защищенном или виртуальном режиме 8086 флаг отключения метки времени (TSD) в регистр CR4 ограничивает использование команды RDTSC следующим образом. Когда флаг TSD ясно, что команда RDTSC может выполняться на любом уровне привилегий; когда флаг задано, инструкция может быть выполнена только на уровне привилегий 0. (В режиме реального адреса В режиме RDTSC инструкция всегда включена.)

Edit:

Отвечая на комментарий AIX, я объясняю, почему TSD, скорее всего, является причиной здесь.

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

  1. Запуск под эмулятором,
  2. с использованием самостоятельно модифицированного кода,
  3. переключатель контекста,
  4. переключатель ядра.

Первые 2 причины обычно не могут задерживать выполнение более чем на несколько сотен циклов. 2000-2500 циклов являются более типичными для переключения контекста / ядра. Но практически невозможно поймать переключение контекста несколько раз в одном и том же месте. Так что это должен быть переключатель ядра. Это означает, что либо программа работает под отладчиком, либо RDTSC не разрешен в пользовательском режиме.

Наиболее вероятной причиной отключения ОС RDTSC может быть безопасность. Были попытки использовать RDTSC для взлома программ шифрования.

1 голос
/ 31 октября 2012

Инструкция кеша отсутствует? (это мое предположение)

Также, возможно,

Переключиться на гипервизор в виртуализированной системе? Остатки программы начальной загрузки (включая сетевую активность на одном и том же процессоре)?

Танатосу: В системах более поздних, чем 2008, rdtsc () является настенными часами и не изменяется с частотой шагов.

Можете ли вы попробовать этот маленький код?

int main()
{   
    long long res;

    fflush(stdout);           // chnage the exact timing of stdout, in case there is something to write in a ssh connection, together with its interrupts

    for (int pass = 0; pass < 2; pass++)
    {
    res=tick();
    res=tick()-res;
    }
    printf("%d",res);     // ignore result on first pass, display the result on second pass.
    return 0;
}
0 голосов
/ 30 ноября 2011

Просто идея - может быть, эти две инструкции rdtsc выполняются на разных ядрах?Значения rdtsc могут незначительно отличаться в зависимости от ядра.

...