Рассмотрим следующий сегмент кода:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#define ARRAYSIZE(arr) (sizeof(arr)/sizeof(arr[0]))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);
}
inline int func() { return 5;}
inline void test()
{
uint64_t start, end;
char c;
start = rdtsc();
func();
end = rdtsc();
printf("%ld ticks\n", end - start);
}
void flushFuncCache()
{
// Assuming function to be not greater than 320 bytes.
char* fPtr = (char*)func;
clflush(fPtr);
clflush(fPtr+64);
clflush(fPtr+128);
clflush(fPtr+192);
clflush(fPtr+256);
}
int main(int ac, char **av)
{
test();
printf("Function must be cached by now!\n");
test();
flushFuncCache();
printf("Function flushed from cache.\n");
test();
printf("Function must be cached again by now!\n");
test();
return 0;
}
Здесь я пытаюсь очистить кэш команд, чтобы удалить код для «func», а затем ожидаю снижения производительности при следующем вызове func, но мои результаты не соответствуют моим ожиданиям:
858 ticks
Function must be cached by now!
788 ticks
Function flushed from cache.
728 ticks
Function must be cached again by now!
710 ticks
я ожидал CLFLUSH также очистить кеш инструкций, но, видимо, он этого не делает. Может кто-то объяснить это поведение или предложить, как добиться желаемого поведения.
Ваш код почти ничего не делает в func
и мало что вы делаете, встраивается в test
и, вероятно, оптимизирован, так как вы никогда не используете возвращаемое значение.
GCC -O3 дает мне —
0000000000400620 <test>:
400620: 53 push %rbx
400621: 0f a2 cpuid
400623: 0f 31 rdtsc
400625: 48 89 d7 mov %rdx,%rdi
400628: 48 89 c6 mov %rax,%rsi
40062b: 0f a2 cpuid
40062d: 0f 31 rdtsc
40062f: 5b pop %rbx
...
Таким образом, вы измеряете время для двух ходов, которые очень дешевы с точки зрения HW — ваши измерения, вероятно, показывают задержку cpuid
что относительно дорого
Хуже твоего clflush
на самом деле покраснеть test
кроме того, это означает, что вы платите штраф за повторный выбор при следующем доступе к нему, что выходит за rdtsc
пара, так что не измеряется. Измеренный код, с другой стороны, последовательно следует, так что выборка test
вероятно, будет также извлекать очищенный код, который вы измеряете, так что он может быть действительно кэширован к тому времени, когда вы его измеряете.
264 ticks
Function must be cached by now!
258 ticks
Function flushed from cache.
519 ticks
Function must be cached again by now!
240 ticks