Я бы хотел увидеть эффект ложного обмена.Для этого я попытался разработать небольшой эксперимент, но получил неожиданные результаты.
У меня есть массив, содержащий 100 м целых чисел.Рассмотрим это как матрицу mxn.Один поток изменяет нечетные индексированные строки, а другой поток изменяет четные индексированные строки.
Эксперимент A: Количество столбцов равно 16. Таким образом, каждая строка составляет 64 байта, это точно мой размер строки кэша.Поскольку каждый поток обрабатывает ровно 1 кэш-строку за раз, не должно быть ложного разделения.Поэтому я ожидаю ускорения примерно на 100%.
Эксперимент B: Количество столбцов равно 8. Каждый поток изменяет 32 байта за раз, что составляет половину кэш-строки.Например, если поток 1 обрабатывает строку 33, данные должны быть перенесены из потока 0, поскольку поток 1 уже обработал строку 32, которая находится в той же строке кэша.(или наоборот, порядок не имеет значения).Из-за этого сообщения скорость должна быть низкой.
#include <iostream>
#include <omp.h>
using namespace std;
int main(int argc, char** argv) {
if(argc != 3) {
cout << "Usage: " << argv[0] << " <iteration> <col_count>" << endl;
return 1;
}
int thread_count = omp_get_max_threads();
int iteration = atoi(argv[1]);
int col_count = atoi(argv[2]);
int arr_size = 100000000;
int* A = (int*) aligned_alloc(16 * sizeof(int), arr_size * sizeof(int));
int row_count = arr_size / col_count;
int row_count_per_thread = row_count / thread_count;
#pragma omp parallel
{
int thread_id = omp_get_thread_num();
long long total = 1ll * iteration * row_count_per_thread * col_count;
printf("%lld\n", total);
for(int t = 0; t < iteration; t++) {
for(int i = 0; i < row_count_per_thread; i++) {
int start = (i * thread_count + thread_id) * col_count;
for(int j = start; j < start + col_count; j++) {
if(A[j] % 2 == 0)
A[j] += 3;
else
A[j] += 1;
}
}
}
}
return 0;
}
Я запускаю этот код с различными конфигурациями следующим образом:
time taskset -c 0-1 ./run 100 16
Вот результатыдля 100 итераций:
Thread Column Optimization Time (secs)
_______________________________________________________
1 16 O3 7.6
1 8 O3 7.7
2 16 O3 7.7
2 8 O3 7.7
1 16 O0 35.9
1 8 O0 34.3
2 16 O0 19.3
2 8 O0 18.2
Как вы можете видеть, хотя оптимизация O3 дает наилучшие результаты, они очень странные, поскольку увеличение количества потоков не приводит к ускорению.Для меня результаты оптимизации O0 более интерпретируемы.
Реальный вопрос: посмотрите на последние 2 строки.В обоих случаях я получил ускорение почти на 100%, однако я ожидаю, что время выполнения эксперимента B должно быть намного выше, поскольку у него есть проблема ложного обмена.Что не так с моим экспериментом или моим пониманием?
Я скомпилировал его с g++ -std=c++11 -Wall -fopenmp -O0 -o run -Iinc $(SOURCE)
и g++ -std=c++11 -Wall -fopenmp -O3 -o run -Iinc $(SOURCE)
Дайте мне знать, если моя проблема не ясна или вам нужно больше подробностей.
Обновление: Характеристики:
MemTotal: 8080796 kB
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 8
On-line CPU(s) list: 0-7
Thread(s) per core: 2
Core(s) per socket: 4
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 71
Model name: Intel(R) Core(TM) i7-5700HQ CPU @ 2.70GHz
Stepping: 1
CPU MHz: 2622.241
CPU max MHz: 3500,0000
CPU min MHz: 800,0000
BogoMIPS: 5387.47
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 6144K
NUMA node0 CPU(s): 0-7
Обновление 2: Я пробовал разные параметры iteration_count
и arr_size
, чтобы массивумещается в кэшах L2, L1, при этом общее число изменений элемента остается постоянным.Но результаты все те же.
Спасибо.