Предотвращение ложного обмена без использования отступов
В настоящее время я узнаю о pthreads
в C и столкнулся с проблемой ложного обмена. Я думаю, что понимаю концепцию этого, и я попытался немного поэкспериментировать.
Ниже короткая программа, с которой я играл. В конце концов, я собираюсь преобразовать его в программу, чтобы получить большой массив целых чисел и суммировать его параллельно.
#include <stdio.h>
#include <pthread.h>
#define THREADS 4
#define NUMPAD 14
struct s
{
int total; // 4 bytes
int my_num; // 4 bytes
int pad[NUMPAD]; // 4 * NUMPAD bytes
} sum_array[4];
static void *worker(void * ind) {
const int curr_ind = *(int *) ind;
for (int i = 0; i < 10; ++i) {
sum_array[curr_ind].total += sum_array[curr_ind].my_num;
}
printf("%d\n", sum_array[curr_ind].total);
return NULL;
}
int main(void) {
int args[THREADS] = { 0, 1, 2, 3 };
pthread_t thread_ids[THREADS];
for (size_t i = 0; i < THREADS; ++i) {
sum_array[i].total = 0;
sum_array[i].my_num = i + 1;
pthread_create(&thread_ids[i], NULL, worker, &args[i]);
}
for (size_t i = 0; i < THREADS; ++i) {
pthread_join(thread_ids[i], NULL);
}
}
У меня вопрос, возможно ли предотвратить ложный обмен без использования отступов? Вот struct s
имеет размер 64 байта, поэтому каждая структура находится в отдельной строке кэша (при условии, что строка кэша составляет 64 байта). Я не уверен, как еще я могу достичь параллелизма без заполнения.
Кроме того, если бы я суммировал массив различного размера между 1000-50 000 байтов, как я мог предотвратить ложное совместное использование? Смогу ли я дополнить его с помощью аналогичной программы? Мои текущие мысли состоят в том, чтобы поместить каждое int из большого массива в массив struct s
а затем использовать параллелизм для суммирования. Однако я не уверен, что это оптимальное решение.
1 ответ
Разделите проблему: в worker()
, суммируйте в локальную переменную, затем добавьте локальную переменную в массив:
static void *worker(void * ind) {
const int curr_ind = *(int *) ind;
int localsum = 0;
for (int i = 0; i < 10; ++i) {
localsum += sum_array[curr_ind].my_num;
}
sum_array[curr_ind].total += localsum;
printf("%d\n", sum_array[curr_ind].total);
return NULL;
}
Это может все еще иметь ложное совместное использование после цикла, но это один раз на поток. Затраты на создание потоков гораздо более значительны, чем одиночная ошибка кэша. Конечно, вы, вероятно, хотите иметь цикл, который на самом деле отнимает много времени, так как ваш текущий код может быть оптимизирован для:
static void *worker(void * ind) {
const int curr_ind = *(int *) ind;
int localsum = 10 * sum_array[curr_ind].my_num;
sum_array[curr_ind].total += localsum;
printf("%d\n", sum_array[curr_ind].total);
return NULL;
}
Во время выполнения которого определенно преобладают создание потока и синхронизация в printf()
,