Вопрос

#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
Это было полезно?

Решение

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

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

Другие советы

Есть любое количество причин, чтобы получить большое количество:

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

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

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

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


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


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

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

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

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

Редактировать:

Отвечая на комментарий aix, поясняю, почему здесь, скорее всего, причина ТСД.

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

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

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

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

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

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

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

To Thanatos: в системах более недавних, чем 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;
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top