Я написал программу для чтения массива 256 КБ, чтобы получить задержку 1 мс. Программа довольно проста и привязана.
Однако, когда я запустил его на ВМ в Xen, я обнаружил, что задержка не стабильна. Он имеет следующий шаблон: единица времени — мс.
#totalCycle CyclePerLine totalms
22583885 5513 6.452539
3474342 848 0.992669
3208486 783 0.916710
25848572 6310 7.385306
3225768 787 0.921648
3210487 783 0.917282
25974700 6341 7.421343
3244891 792 0.927112
3276027 799 0.936008
25641513 6260 7.326147
3531084 862 1.008881
3233687 789 0.923911
22397733 5468 6.399352
3523403 860 1.006687
3586178 875 1.024622
26094384 6370 7.455538
3540329 864 1.011523
3812086 930 1.089167
25907966 6325 7.402276
Я думаю, что какой-то процесс что-то делает, и это похоже на процесс, управляемый событиями. Кто-нибудь сталкивался с этим раньше? или кто-нибудь может указать на потенциальные процессы / услуги, которые могли бы сделать это?
Ниже моя программа. Я запускаю его 1000 раз. Каждый раз получаю одну строчку результата выше.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <string>
#include <ctime>
using namespace std;
#if defined(__i386__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned long long int x;
__asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
return x;
}
#elif defined(__x86_64__)
static __inline__ unsigned long long rdtsc(void)
{
unsigned hi, lo;
__asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}
#endif
#define CACHE_LINE_SIZE 64
#define WSS 24567 /* 24 Mb */
#define NUM_VARS WSS * 1024 / sizeof(long)
#define KHZ 3500000
// ./a.out memsize(in KB)
int main(int argc, char** argv)
{
unsigned long wcet = atol(argv[1]);
unsigned long mem_size_KB = 256; // mem size in KB
unsigned long mem_size_B = mem_size_KB * 1024; // mem size in Byte
unsigned long count = mem_size_B / sizeof(long);
unsigned long row = mem_size_B / CACHE_LINE_SIZE;
int col = CACHE_LINE_SIZE / sizeof(long);
unsigned long long start, finish, dur1;
unsigned long temp;
long *buffer;
buffer = new long[count];
// init array
for (unsigned long i = 0; i < count; ++i)
buffer[i] = i;
for (unsigned long i = row-1; i >0; --i) {
temp = rand()%i;
swap(buffer[i*col], buffer[temp*col]);
}
// warm the cache again
temp = buffer[0];
for (unsigned long i = 0; i < row-1; ++i) {
temp = buffer[temp];
}
// First read, should be cache hit
temp = buffer[0];
start = rdtsc();
int sum = 0;
for(int wcet_i = 0; wcet_i < wcet; wcet_i++)
{
for(int j=0; j<21; j++)
{
for (unsigned long i = 0; i < row-1; ++i) {
if (i%2 == 0) sum += buffer[temp];
else sum -= buffer[temp];
temp = buffer[temp];
}
}
}
finish = rdtsc();
dur1 = finish-start;
// Res
printf("%lld %lld %.6f\n", dur1, dur1/row, dur1*1.0/KHZ);
delete[] buffer;
return 0;
}
Использование инструкции RDTSC в виртуальной машине является сложным. Вполне вероятно, что гипервизор (Xen) эмулирует инструкцию RDTSC, перехватывая ее. Ваши самые быстрые прогоны показывают около 800 циклов / строка кэша, что очень, очень, медленно … единственное объяснение состоит в том, что RDTSC приводит к ловушке, которая обрабатывается гипервизором, что накладные расходы являются узким местом производительности. Я не уверен в том, что вы периодически видите больше времени, но, учитывая, что RDTSC находится в ловушке, все временные ставки отключены.
Вы можете прочитать больше об этом здесь
http://xenbits.xen.org/docs/4.2-testing/misc/tscmode.txt
Инструкции в семействе rdtsc непривилегированы, но
Привилегированное программное обеспечение может установить бит cpuid, чтобы вызвать все семейство rdtsc
инструкции к ловушке. Эта ловушка может быть обнаружена Xen, которая может
затем прозрачно «эмулировать» результаты инструкции rdtsc и
вернуть управление коду, следуя инструкции rdtsc
Кстати, эта статья неверна в том, что гипервизор не устанавливает cpuid bit
чтобы перехватить RDTSC, это бит № 2 в регистре управления 4 (CR4.TSD):
Других решений пока нет …