Я делаю некоторые тесты для расчета времени в Linux. Мое ядро - Preempt-RT (однако в моих тестах ванильное ядро дает похожие результаты ...)
У меня есть два файла pthreads, работающие одновременно на одном и том же процессоре (дано сродство). Это потоки в реальном времени (prio 99).
У меня есть критическая секция, защищенная спин-блокировкой, где два потока конкурируют за блокировку. Внутри критической секции у меня есть операция с одним приращением, и я пытаюсь вычислить истекшее время для этой операции.
Пример кода с __rdtsc:
pthread_spin_lock(&lock);
start_time = __rdtsc();
++cnt; //shared ram variable, type is unsigned long long
stop_time = __rdtsc();
pthread_spin_unlock(&lock);
Пример кода с таймером хронографа:
pthread_spin_lock(&lock);
auto _start_time = std::chrono::high_resolution_clock::now();
++cnt; //shared ram variable, type is unsigned long long
auto _stop_time = std::chrono::high_resolution_clock::now();
pthread_spin_unlock(&lock);
Потоки запускаются в цикле пару миллионов раз и затем завершаются. После разблокировки спин-блокировки я записываю среднее затраченное время и максимальное истекшее время.
Теперь все идет интересно (по крайней мере, для меня):
Тест 1: Потоки имеют политику планирования как SCHED_RR :
Резьба №: 0, максимальное время: 34124, среднее время: 28.114271, Cnt запуска: 10000000
Резьба №: 1, Макс. Время: 339256976 , Среднее время: 74.781960, Cnt запуска: 10000000
Тест 2. У потоков есть политика планирования: SCHED_FIFO :
Резьба №: 0, Макс. Время: 33114, Среднее время: 48,414173, Cnt запуска: 10000000
Резьба №: 1, максимальное время: 38637, среднее время: 24.327742, Cnt запуска: 10000000
Тест 3: только один поток, политика планирования SCHED_RR:
Резьба №: 0, максимальное время: 34584, среднее время: 54,165470, Cnt запуска: 10000000
Примечание. Основной поток - это не относящийся к rt поток, имеющий сходство с отдельным процессором. Здесь это не важно.
Примечание 1: Все тесты дают ок. подобные результаты каждый раз, когда я запускаю их.
Примечание 2: Данные результаты являются выходными данными rdtsc. Тем не менее, результаты хронографа почти аналогичны этим.
Так что я думаю, что у меня неправильное понимание планировщика, поэтому мне нужно задать следующие вопросы:
- Как возникают максимальные максимальные временные пики в тесте 1? Тест 2 и 3 не ведут себя так, как он ...
- Почему существует очень большой разрыв между максимальным и средним расчетами? Что вызывает это, прерывание как таймер?
Мой весь тестовый код:
#include <stdio.h>
#include <stdlib.h>
#include "stdint.h"
#include <float.h>
#include <pthread.h>
#include <cxxabi.h>
#include <limits.h>
#include <sched.h>
#include <sys/mman.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <malloc.h>
#include <chrono>
/********* TEST CONFIG ************/
#define TEST_PTHREAD_RUN_CNT 10000000 //1000000000
#define NUM_OF_TEST_PTHREADS 2
#define MAIN_THREAD_CORE_INDEX 0
#define TEST_PTHREAD_PRIO 99
#define TEST_PTHREAD_POLICY SCHED_RR
#define TIME_RDTSC 1
#define TIME_CHRONO 0
/**********************************/
/**********************************/
struct param_list_s
{
unsigned int thread_no;
};
/**********************************/
/********* PROCESS RAM ************/
pthread_t threads[NUM_OF_TEST_PTHREADS];
struct param_list_s param_list[NUM_OF_TEST_PTHREADS];
unsigned long long max_time[NUM_OF_TEST_PTHREADS];
unsigned long long _max_time[NUM_OF_TEST_PTHREADS];
unsigned long long tot_time[NUM_OF_TEST_PTHREADS];
unsigned long long _tot_time[NUM_OF_TEST_PTHREADS];
unsigned long long run_cnt[NUM_OF_TEST_PTHREADS];
unsigned long long cnt;
pthread_spinlock_t lock;
/**********************************/
/*Proto*/
static void configureMemoryBehavior(void);
void create_rt_pthread(unsigned int thread_no);
/*
* Date............:
* Function........: main
* Description.....:
*/
int main(void)
{
cpu_set_t mask;
int i;
for (i = 0; i < NUM_OF_TEST_PTHREADS; ++i)
{
max_time[i] = 0;
tot_time[i] = 0;
run_cnt[i] = 0;
_max_time[i] = 0;
_tot_time[i] = 0;
}
cnt = 0;
printf("\nSetting scheduler affinity for the process...");
CPU_ZERO(&mask);
CPU_SET(MAIN_THREAD_CORE_INDEX, &mask);
sched_setaffinity(0, sizeof(mask), &mask);
printf("done.\n");
configureMemoryBehavior();
pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE);
for (i = 0; i < NUM_OF_TEST_PTHREADS; ++i)
{
create_rt_pthread(i);
}
printf("Waiting threads to join\n...\n");
for (i = 0; i < NUM_OF_TEST_PTHREADS; i++)
{
pthread_join(threads[i], NULL);
#if(TIME_RDTSC == 1)
printf("Thread no: %d, Max Time: %llu, Avg Time: %f, Run Cnt: %llu\n", i, max_time[i], (float)((float)tot_time[i] / run_cnt[i]), run_cnt[i]);
#endif
#if(TIME_CHRONO == 1)
printf("Thread no: %d, Max Time: %lu, Avg Time: %f, Run Cnt: %lu\n", i, _max_time[i], (float)((float)_tot_time[i] / run_cnt[i]), run_cnt[i]);
#endif
}
printf("All threads joined\n");
printf("Shared Cnt: %llu\n", cnt);
return 0;
}
/*
* Date............:
* Function........: thread_func
* Description.....:
*/
void *thread_func(void *argv)
{
unsigned long long i, start_time, stop_time, latency = 0;
unsigned int thread_no;
thread_no = ((struct param_list_s *)argv)->thread_no;
i = 0;
while (1)
{
#if(TIME_RDTSC == 1)
pthread_spin_lock(&lock);
start_time = __rdtsc();
++cnt;
stop_time = __rdtsc();
pthread_spin_unlock(&lock);
if (stop_time > start_time)
{
latency = stop_time - start_time;
++run_cnt[thread_no];
tot_time[thread_no] += latency;
if (latency > max_time[thread_no])
max_time[thread_no] = latency;
}
#endif
#if(TIME_CHRONO == 1)
pthread_spin_lock(&lock);
auto _start_time = std::chrono::high_resolution_clock::now();
++cnt;
auto _stop_time = std::chrono::high_resolution_clock::now();
pthread_spin_unlock(&lock);
auto __start_time = std::chrono::duration_cast<std::chrono::nanoseconds>(_start_time.time_since_epoch()).count();
auto __stop_time = std::chrono::duration_cast<std::chrono::nanoseconds>(_stop_time.time_since_epoch()).count();
auto __latency = __stop_time - __start_time;
if (__stop_time > __start_time)
{
_tot_time[thread_no] += __latency;
++run_cnt[thread_no];
if (__latency > _max_time[thread_no])
{
_max_time[thread_no] = __latency;
}
}
#endif
if (++i >= TEST_PTHREAD_RUN_CNT)
break;
}
return 0;
}
/*
* Date............:
* Function........: create_rt_pthread
* Description.....:
*/
void create_rt_pthread(unsigned int thread_no)
{
struct sched_param param;
pthread_attr_t attr;
printf("Creating a new real-time thread\n");
/* Initialize pthread attributes (default values) */
pthread_attr_init(&attr);
/* Set a specific stack size */
pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
/* Set scheduler policy and priority of pthread */
pthread_attr_setschedpolicy(&attr, TEST_PTHREAD_POLICY);
param.sched_priority = TEST_PTHREAD_PRIO;
pthread_attr_setschedparam(&attr, ¶m);
/* Set the processor affinity*/
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
/* Use scheduling parameters of attr */
pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
param_list[thread_no].thread_no = thread_no;
if(pthread_create(&threads[thread_no], &attr, thread_func, (void *)¶m_list[thread_no]) != 0)
{
printf("Thread could not be created.\n");
exit(-1);
}
}
/*
* Date............:
* Function........: configureMemoryBehavior
* Description.....:
*/
static void configureMemoryBehavior(void)
{
printf("\nLocking memory...");
/* Now lock all current and future pages
from preventing of being paged */
if (mlockall(MCL_CURRENT | MCL_FUTURE))
perror("mlockall failed:");
/* Turn off malloc trimming.*/
mallopt(M_TRIM_THRESHOLD, -1);
/* Turn off mmap usage. */
mallopt(M_MMAP_MAX, 0);
printf("done.\n");
}