Точность clock_gettime() в сценарии переключения контекста

Я пытаюсь "грубо" рассчитать время переключения контекста потока в системе Linux. Я написал программу, которая использует каналы и многопоточность для достижения этой цели. При запуске программы рассчитанное время явно неверно (см. Вывод ниже). Я не уверен, что это из-за того, что я использовал неправильный clock_id для этой процедуры или, возможно, мою реализацию

Я реализовал sched_setaffinity(), чтобы программа работала только на ядре 0. Я попытался оставить столько кода в коде, чтобы измерить только время переключения контекста, поэтому процесс протектора записывает только один символ в трубу и родитель делает чтение 0 байт.

У меня есть родительский протектор, который создает один дочерний поток с односторонним каналом между ними для передачи данных, дочерний поток выполняет простую функцию для записи в канал.

    void* thread_1_function()
    {
         write(fd2[1],"",sizeof("");
    }

в то время как родительский поток создает дочерний поток, запускает счетчик времени и затем вызывает чтение в канале, в который записывает дочерний поток.

int main(int argc, char argv[])
{
//time struct declaration
struct timespec start,end;

//sets program to only use core 0
cpu_set_t cpu_set;
CPU_ZERO(&cpu_set);
CPU_SET(0,&cpu_set);


if((sched_setaffinity(0, sizeof(cpu_set_t), &cpu_set) < 1))
{

int nproc = sysconf(_SC_NPROCESSORS_ONLN);
int k;

printf("Processor used: ");
for(k = 0; k < nproc; ++k)
{
    printf("%d ", CPU_ISSET(k, &cpu_set));
}

printf("\n");


if(pipe(fd1) == -1)
{
    printf("fd1 pipe error");
    return 1;
}
//fail on file descriptor 2 fail
if(pipe(fd2) == -1)
{
    printf("fd2 pipe error");
    return 1;
}


pthread_t thread_1;


pthread_create(&thread_1, NULL, &thread_1_function, NULL);


pthread_join(thread_1,NULL);


int i;
uint64_t sum = 0;

for(i = 0; i < iterations; ++i)
{

    //initalize clock start
    clock_gettime(CLOCK_MONOTONIC, &start);
    //wait for child thread to write to pipe
    read(fd2[0],input,0);
    //record clock end
    clock_gettime(CLOCK_MONOTONIC, &end);   

    write(fd1[1],"",sizeof(""));



    uint64_t diff;
    diff = billion * (end.tv_sec - start.tv_sec) + end.tv_nsec - start.tv_nsec;
    diff = diff;
    sum += diff;
}

Результаты, которые я получаю во время работы, обычно таковы:

     3000
     3000
     4000
     2000
     12000
     3000
     5000

и так далее, когда я проверяю время, возвращаемое в начальную и конечную структуры timepec, я вижу, что tv_nsec также представляется "округленным" числом:

     start.tv_nsec: 714885000, end.tv_nsec: 714888000

Будет ли это вызвано тем, что clock_monotonic недостаточно точен для того, что я пытаюсь измерить, или какой-то другой проблемой, которую я пропускаю?

1 ответ

я вижу, что tv_nsec также кажется округленным числом:

 2626, 714885000, 2626, 714888000

Будет ли это вызвано тем, что clock_monotonic недостаточно точен для того, что я пытаюсь измерить, или какой-то другой проблемой, которую я пропускаю?

Да, это возможно. Каждые часы, поддерживаемые системой, имеют фиксированное разрешение. struct timespec способен поддерживать часы с наносекундным разрешением, но это не значит, что вы можете ожидать, что у всех часов будет такое разрешение. Похоже, ваш CLOCK_MONOTONIC может иметь разрешение 1 мкс (1000 наносекунд), но вы можете проверить это с помощью clock_getres() функция.

Если это доступно для вас, то вы можете попробовать CLOCK_PROCESS_CPUTIME_ID, Вполне возможно, что это будет иметь более высокое разрешение, чем CLOCK_MONOTONIC для вас, но обратите внимание, что разрешение в одну микросекунду является довольно точным - это порядка одного такта на 3000 циклов ЦП на современной машине.


Несмотря на это, я вижу несколько возможных проблем с вашим подходом:

  • Несмотря на то, что вы настроили свой процесс на привязку к одному ЦП, это не мешает системе планировать и другие процессы на этом ЦП. Таким образом, если вы не предприняли дополнительных мер, вы не можете быть уверены - даже маловероятно - что каждый контекст переключается с одного из потоков вашей программы на другой поток.

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

  • read() со счетчиком 0 может проверять или не проверять наличие ошибок, и, безусловно, не передает никаких данных. Мне совершенно непонятно, почему вы идентифицируете время для этого вызова со временем для переключения контекста.

  • Если переключение контекста действительно происходит в пространстве, которое вы синхронизируете, тогда должно быть как минимум два - от вашей программы и обратно к ней. Кроме того, вы измеряете время, затрачиваемое на то, что еще работает в другом контексте, а не только на время переключения. Таким образом, шаги в 1000 наносекунд могут отражать временные интервалы, а не время переключения.

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

Другие вопросы по тегам