clflush не дает правильного ответа в i7 для типа данных const

Я написал две программы, чтобы проверить, удаляет ли clflush мои данные из кэша или нет. Из двух программ, которые я написал, только одна дает правильный результат (согласно моим ожиданиям, после clflush время доступа должно быть выше, чем до сброса).

Вот моя программа1, где я получаю ожидаемый результат.

#include <stdio.h>
#include <stdint.h>

inline void clflush(volatile void *p)
{
asm volatile ("clflush (%0)" :: "r"(p));
}

inline uint64_t rdtsc()
{
unsigned long a, d;
asm volatile ("cpuid; rdtsc" : "=a" (a), "=d" (d) : : "ebx", "ecx");
return a | ((uint64_t)d << 32);
}static int i=10; // static variable

inline void test()
{
uint64_t start, end;
int j;
start = rdtsc();
j = i;
end = rdtsc();
printf("took %lu ticks\n", end - start);
}

int main(int ac, char **av)
{
test();
test();
printf("flush: ");
clflush((void *)&i);
test();
test();
return 0;
}

Вот мой вывод (как и ожидалось)

took 314 ticks
took 282 ticks
flush: took 442 ticks
took 272 ticks

Вот еще одна программа, где Я не получаю ожидаемого результата.

 #include <stdio.h>
#include <stdint.h>

inline void clflush(volatile void *p)
{
asm volatile ("clflush (%0)" :: "r"(p));
}

inline uint64_t rdtsc()
{
unsigned long a, d;
asm volatile ("cpuid; rdtsc" : "=a" (a), "=d" (d) : : "ebx", "ecx");
return a | ((uint64_t)d << 32);
}static const int i=10; // I make this as constant

inline void test()
{
uint64_t start, end;
int j;
start = rdtsc();
j = i;
end = rdtsc();
printf("took %lu ticks\n", end - start);
}

int main(int ac, char **av)
{
test();
test();
printf("flush: ");
clflush((void *)&i);
test();
test();
return 0;
}

Вот мой вывод (как и ожидалось)

took 314 ticks
took 282 ticks
flush: took 282 ticks // same as previous
took 272 ticks--------
took 314 ticks
took 282 ticks
flush: took 272 ticks // lower than previous
took 272 ticks

Если я сделаю статический int я = 10; в статическое const int i = 10; тогда результат не соответствует моим ожиданиям. Я получаю меньшее значение / равное время доступа после clflush.

Кто-нибудь может объяснить, почему это происходит? Как я могу сделать это (в C или C ++) в соответствии с моими ожиданиями (более высокое время доступа после clflush как program1)?

Я использую GCC под Fedora19 Linux. Любая помощь будет высоко оценена.

1

Решение

Я уверен, что проблема в том, что CPUID + RDTSC слишком длинный, по сравнению с «инструкциями между».

Я получаю очень разные результаты, предположительно в зависимости от «удачи» того, на каком фактическом процессоре выполняется код, что делает другой процессор и т. Д. И т. Д.

Вот три прогона подряд второй программы:

took 92 ticks
took 75 ticks
flush: took 75 ticks
took 474 ticks

took 221 ticks
took 243 ticks
flush: took 221 ticks
took 242 ticks

took 221 ticks
took 221 ticks
flush: took 221 ticks
took 230 ticks

Однако я не думаю, что из этого можно сделать вывод, что «clflush не работает». Только то, что в процессоре достаточно тактов и неиспользуемого исполнения, чтобы преодолеть очистку кеша и перезагрузить данные.

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

#include <stdio.h>
#include <stdint.h>

inline void clflush(volatile void *p)
{
__asm volatile ("clflush (%0)" :: "r"(p));
}

inline uint64_t rdtsc()
{
unsigned long a, d;
__asm volatile ("rdtsc" : "=a" (a), "=d" (d) : : "ebx", "ecx");
return a | ((uint64_t)d << 32);
}static int v[1024];
uint64_t   t[5];
int        r[5];
int        ti = 0;

static inline void test()
{
uint64_t start, end;
int j;
start = rdtsc();
for(int i = 0; i < 1024; i++)
{
j += v[i];
}
end = rdtsc();
r[ti] = j;
t[ti++] = end - start;
}

int main(int ac, char **av)
{
for(int i = 0; i < 1024; i++)
{
v[i] = i;
}
test();
test();
t[ti++] = 0;
for(int i = 0; i < 1024; i+=4)
{
clflush((void *)&v[i]);
}
test();
test();
for(int i = 0; i < ti; i++)
{
if (t[i] == 0)
{
printf("flush\n");
}
else
{
printf("Test %lu [res=%d]\n", t[i], r[i]);
}
}
printf("\n");
return 0;
}

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

Test 2538 [res=523776]
Test 2593 [res=523776]
flush
Test 4845 [res=523776]
Test 2592 [res=523776]

Test 2550 [res=523776]
Test 2771 [res=523776]
flush
Test 4782 [res=523776]
Test 2513 [res=523776]

Test 2550 [res=523776]
Test 2708 [res=523776]
flush
Test 4356 [res=523776]
Test 2593 [res=523776]

Как вы можете видеть, после сброса данные примерно в два раза быстрее извлекаются по сравнению с первым доступом.

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

Используя const, вот так

static const int v[1024] =
{
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
/* snip 62 lines equal to this */
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
};

дает такой результат:

Test 14139 [res=8704]
Test 2639 [res=8704]
flush
Test 5287 [res=8704]
Test 2597 [res=8704]

Test 12983 [res=8704]
Test 2652 [res=8704]
flush
Test 4859 [res=8704]
Test 2550 [res=8704]

Test 12911 [res=8704]
Test 2581 [res=8704]
flush
Test 4705 [res=8704]
Test 2649 [res=8704]

Как видите, третий доступ явно медленнее, чем второй и четвертый. Первый доступ медленнее, потому что при первом доступе в кеше вообще ничего нет (включая таблицы страниц и т. Д.).

1

Другие решения

Других решений пока нет …