Параллельное программирование ложная проблема совместного использования

У меня есть вопрос о ложном разделении параллельного программирования.

Исходный код находится внизу.

Резюмируйте мой вопрос здесь:

если определить 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.

Мои вопросы:

  1. Почему потеря кэша составляет ~ 2 ~ 3% только? Интуитивно я предполагаю, что это будет ~ 50%, так как 2 потока выполняют чередование.

  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;
}
*/

0 ответов

Другие вопросы по тегам