Параллельное программирование ложная проблема совместного использования
У меня есть вопрос о ложном разделении параллельного программирования.
Исходный код находится внизу.
Резюмируйте мой вопрос здесь:
если определить USE_REGISTER,
perf stat -e L1-dcache-load -e L1-dcache-load-misses./falseSharing
а также
#define PADNUM 1
результат
12,0461,8669 L1-dcache-load 2861,8883 L1-dcache-load-misses # 2.38% of all L1-dcache hit
Exe время
реальный 0m0.683s
затем я увеличиваю PADNUM на 1 на 1.
Время exe почти не меняется, пока PADNUM не станет равным 7.
Ускорение является ожидаемым X2, когда PADNUM >= 7, как
реальный 0m0,305s
и в это время отсутствует кеш
12,0115,2333 L1-dcache-load 6,9266 L1-dcache-load-misses # 0.01% of all L1-dcache hits
Если я применил USE_REGISTER, как и ожидалось, некоторые переменные будут сохранены в регистре, поэтому количество L1-dcache-load упадет до ~ 7,0000,0000.
Мои вопросы:
Почему потеря кэша составляет ~ 2 ~ 3% только? Интуитивно я предполагаю, что это будет ~ 50%, так как 2 потока выполняют чередование.
Почему pad только 7? поскольку DUMP_TID говорит, например:
addr (x) = 0x602200, addr (y) = 0x602220, sizeof padding = 28, начало pad в 0x602204, sizeof(f) = 36
это означает, что в моем компьютере заполнение составляет 28 байт, размер float составляет 4 байта, поэтому выравнивание кажется равным 32 байтам вместо 64 байт.
Однако cat cpuinfo сообщает, что строка кэша выровнена с 64 байтами:
cache_alignment: 64
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <iostream>
#include <sstream>
#include <mutex>
#ifdef USE_REGISTER
#define REGISTER register
#else
#define REGISTER
#endif
using namespace std;
#define PADNUM 1
#define REGISTER register
struct fooPad {
float x;
float pad[PADNUM];
float y;
};
fooPad f;
/* The two following functions are running concurrently: */
std::mutex g_display_mutex;
#define DUMP_TID() \
do{ \
g_display_mutex.lock(); \
std::thread::id this_id = std::this_thread::get_id(); \
std::cout << __FUNCTION__ << "() thread = " << this_id << endl; \
g_display_mutex.unlock(); \
} while(0)
float sum_a(void)
{
DUMP_TID();
REGISTER float s = 0;
REGISTER float i;
for (i = 0; i < 10000000; ++i)
s += f.x;
return s;
}
float inc_b(void)
{
DUMP_TID();
REGISTER float i;
for (i = 0; i < 10000000; ++i){
f.y += 1.0;
}
return f.y;
}
int main()
{
float a = 0;
float b = 0;
f.x = 1.0;
f.y = 1.5;
printf("addr(x) = %p, addr(y) = %p, sizeof padding = %ld, pad start at %p, sizeof(f) = %ld\n",
&(f.x),
&(f.y),
sizeof(f.pad),
&(f.pad[0]), sizeof(f) );
for (int i = 0; i < 10; i++) {
#pragma omp parallel num_threads(2)
#pragma omp single
{
#pragma omp task
//a = sum_a();
a = sum_a();
#pragma omp task
//b = inc_b();
b = inc_b();
#pragma omp taskwait
}
}
printf("a = %f, b = %f\n", a, b);
return 1;
}
/*
int main()
{
arr[0] = 1; arr[1] = 2;
float a = 0;
float b = 0;
f.x = 1.0;
f.y = 1.5;
printf("size %ld %ld %ld\n", sizeof(f.x),sizeof(f.y), sizeof(f) );
printf("size %ld %ld %ld\n", sizeof(f1.x),sizeof(f1.y), sizeof(f1) );
printf("size %ld %ld %ld\n", sizeof(f2.x),sizeof(f2.y), sizeof(f2) );
for (int i = 0; i < 10; i++) {
#pragma omp parallel num_threads(1)
#pragma omp single
{
#pragma omp task
a = sum_a();
#pragma omp task
b = inc_b();
#pragma omp taskwait
}
}
printf("a = %f, b = %f\n", a, b);
return 1;
}
*/