Я пытаюсь оптимизировать производительность чтения и записи double
в общую память.У меня одна программа пишет в общую память, а другая читает из нее.
Я использовал этот пост , чтобы помочь изолировать процессоры для этих двух программ для запуска, со следующей строкой в моем etc/default/grub
файле:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash intel_idle.max_cstate=1 isolcpus=6,7"
Я использую taskset -c 6 writer
и taskset -c 7 reader
, чтобы настроить эти программы для запуска на этих процессорах.
Используя эту справочную страницу на sched_setscheduler , я настроил обе программы на самое высокое планированиеПриоритет с использованием следующего кода:
struct sched_param param;
param.sched_priority = sched_get_priority_max(SCHED_FIFO);
if(sched_setscheduler(0, SCHED_FIFO, ¶m) == -1)
{
perror("sched_setscheduler failed");
exit(-1);
}
Я определил структуру, которая будет использоваться в общей памяти, которая содержит необходимые инструменты синхронизации, а также структуру timepec и значение типа double для передачи между двумя программами, так какследует:
typedef struct
{
// Synchronization objects
pthread_mutex_t ipc_mutex;
sem_t ipc_sem;
// Shared data
double value;
volatile int read_cond;
volatile int end_cond;
double start_time;
struct timespec ts;
} shared_data_t;
Инициализация общей памяти:
Писатель:
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
ftruncate(shmid, sizeof(shared_data_t));
// shmat to attach to shared memory
shared_data_t* sdata = (shared_data_t*) shmat(shmid,(void*)0,0);
sdata->value = 0;
Считыватель:
// ftok to generate unique key
key_t key = ftok("shmfile",65);
// shmget returns an identifier in shmid
int shmid = shmget(key,1024,0666|IPC_CREAT);
ftruncate(shmid, sizeof(shared_data_t));
// shmat to attach to shared memory
shared_data_t* sdata = (shared_data_t*) shmat(shmid,(void*)0,0);
Инициализация инструментов синхронизации в Writer
pthread_mutexattr_t mutex_attr;
pthread_mutexattr_init(&mutex_attr);
pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&sdata->ipc_mutex, &mutex_attr);
sem_init(&sdata->ipc_sem, 1, 0);
Код записи
for (int i = 0; i < 20000000; ++i)
{
pthread_mutex_lock(&sdata->ipc_mutex);
sdata->value++;
clock_gettime(CLOCK_MONOTONIC, &sdata->ts);
sdata->start_time = (BILLION*sdata->ts.tv_sec) + sdata->ts.tv_nsec;
sdata->read_cond = 1;
pthread_mutex_unlock(&sdata->ipc_mutex);
sem_wait(&sdata->ipc_sem);
}
fprintf(stderr, "done writing\n" );
pthread_mutex_lock(&sdata->ipc_mutex);
sdata->end_cond = 1;
pthread_mutex_unlock(&sdata->ipc_mutex);
Считывание кода
double counter = 0;
double total_time = 0;
double max_time = 0;
double min_time = BILLION;
double max_thresh = 1000;
int above_max_counter = 0;
double last_val = 0;
while (1) {
pthread_mutex_lock(&sdata->ipc_mutex);
while (!sdata->read_cond && !sdata->end_cond) {
pthread_mutex_unlock(&sdata->ipc_mutex);
pthread_mutex_lock(&sdata->ipc_mutex);
}
clock_gettime(CLOCK_MONOTONIC, &sdata->ts);
double time_to_read = (BILLION*sdata->ts.tv_sec) + sdata->ts.tv_nsec - sdata->start_time;
if (sdata->end_cond) {
break;
}
if (sdata->value != last_val + 1) {
fprintf(stderr, "synchronization error: val: %g, last val: %g\n", sdata->value, last_val);
}
last_val = sdata->value;
if (time_to_read > max_time) {
max_time = time_to_read;
printf("max time: %lf, counter: %ld\n", max_time, (long int) counter);
}
if (time_to_read < min_time) min_time = time_to_read;
if (time_to_read > max_thresh) above_max_counter++;
total_time += time_to_read;
counter++;
sdata->read_cond = 0;
sem_post(&sdata->ipc_sem);
pthread_mutex_unlock(&sdata->ipc_mutex);
}
fprintf(stderr, "avg time to read: %g\n", total_time / counter);
fprintf(stderr, "max time to read: %g\n", max_time);
fprintf(stderr, "min time to read: %g\n", min_time);
fprintf(stderr, "count above max threshhold of %g ns: %d\n", max_thresh, above_max_counter);
Очистка в Writer
//detach from shared memory
shmdt(sdata);
Очистка в считывателе
pthread_mutex_unlock(&sdata->ipc_mutex);
pthread_mutex_destroy(&sdata->ipc_mutex);
//detach from shared memory
shmdt(sdata);
// destroy the shared memory
shmctl(shmid,IPC_RMID,NULL);
Цель состоит в том, чтобы минимизировать количество времени, затрачиваемого на эти две операции.В идеале я хотел бы иметь возможность гарантировать, что время считывания с момента записи значения составляет менее 1 микросекунды.Тем не менее, вывод, который я получаю:
max time: 5852.000000, counter: 0
max time: 18769.000000, counter: 30839
max time: 27416.000000, counter: 66632
max time: 28668.000000, counter: 1820109
max time: 121362.000000, counter: 1853346
done writing
avg time to read: 277.959
max time to read: 121362
min time to read: 60
count above max threshhold of 1000 ns: 1871
, указывает, что в ряде случаев (~ 0,01% операций чтения) считывание превышает 1 единицу и может достигать 121us.
У меня такой вопрос:
Что может быть причиной этих пиков, поскольку я установил приоритет на самый высокий и изолировал ЦП, на котором работают эти программы?
Я узнал из этого поста , что не следует ожидать, что clock_gettime будет иметь наносекундную точность.Являются ли эти пики просто неточностями в clock_gettime?
Другой вариант, который я рассмотрел, заключается в том, что эти ядра (6 и 7) каким-то образом прерываются, несмотря на то, что они были установлены в качестве наивысшего приоритета.
Любая помощь будетс благодарностью.
EDIT
В комментариях ниже приведено содержимое моего /proc/interrupts
файла:
CPU0 CPU1 CPU2 CPU3 CPU4 CPU5 CPU6 CPU7
0: 20 0 0 0 0 0 0 0 IO-APIC 2-edge timer
1: 2 0 0 0 0 0 0 0 IO-APIC 1-edge i8042
8: 1 0 0 0 0 0 0 0 IO-APIC 8-edge rtc0
9: 0 0 0 0 0 0 0 0 IO-APIC 9-fasteoi acpi
12: 2 0 0 0 1 1 0 0 IO-APIC 12-edge i8042
16: 0 0 0 0 0 0 0 0 IO-APIC 16-fasteoi i801_smbus, pcim_das1602_16
19: 2 0 0 0 8 10 6 2 IO-APIC 19-fasteoi
120: 0 0 0 0 0 0 0 0 PCI-MSI 16384-edge aerdrv
121: 99 406 0 0 14 5960 6 0 PCI-MSI 327680-edge xhci_hcd
122: 8726 133 47 28 4126 3910 22638 795 PCI-MSI 376832-edge ahci[0000:00:17.0]
123: 2 0 0 0 2 0 3 3663 PCI-MSI 520192-edge eno1
124: 3411 0 2 1 176 24498 77 11 PCI-MSI 32768-edge i915
125: 45 0 0 0 3 6 0 0 PCI-MSI 360448-edge mei_me
126: 432 0 0 0 144 913 28 1 PCI-MSI 514048-edge snd_hda_intel:card0
NMI: 1 1 1 1 1 1 1 1 Non-maskable interrupts
LOC: 12702 10338 10247 10515 9969 10386 16658 13568 Local timer interrupts
SPU: 0 0 0 0 0 0 0 0 Spurious interrupts
PMI: 1 1 1 1 1 1 1 1 Performance monitoring interrupts
IWI: 0 0 0 0 0 0 0 0 IRQ work interrupts
RTR: 7 0 0 0 0 0 0 0 APIC ICR read retries
RES: 4060 2253 1026 708 595 846 887 751 Rescheduling interrupts
CAL: 11906 10423 11418 9894 14562 11000 21479 11223 Function call interrupts
TLB: 10620 8996 10060 8674 13172 9622 20121 9838 TLB shootdowns
TRM: 0 0 0 0 0 0 0 0 Thermal event interrupts
THR: 0 0 0 0 0 0 0 0 Threshold APIC interrupts
DFR: 0 0 0 0 0 0 0 0 Deferred Error APIC interrupts
MCE: 0 0 0 0 0 0 0 0 Machine check exceptions
MCP: 2 2 2 2 2 2 2 2 Machine check polls
ERR: 0
MIS: 0
PIN: 0 0 0 0 0 0 0 0 Posted-interrupt notification event
PIW: 0 0 0 0 0 0 0 0 Posted-interrupt wakeup event
Имеюпопытался изменить сходство smp для прерываний 122 и 123 на ядра 0 и 1, для этого поста , который, похоже, ничего не делает, так как, когда я перезагружаю свой компьютер, эти сходства все еще устанавливаются на ядра 6 и 7,
Даже без сброса и простого повторного запуска моих программ я не вижу изменений в количестве прерываний, обслуживаемых этими ядрами ЦП.