2016-10-10 2 views
0

У меня проблема с многопоточным кодом, когда я использую его на определенном сервере с 2-мя ЦП. Сервер работает на Windows 7 x64 с Bi-Xeon E5-2697Wv2 12 ядер 2,7 ГГц; ОЗУ 64 Гб (8X8 Гб 1866 МГц); материнская плата SuperMicro X9DAI. Мой исполняемый файл сгенерирован с использованием Visual Studio MSVC 2013 и многопоточным с использованием OpenMP.Производительность распределения памяти двух CPU

Теперь проблема заключается в том, что у меня есть более высокая производительность, используя 1 поток в сравнении 24 темы ... Эта проблема видна только на этом компьютере, и когда я прикрепил профилировщик (CodeXL) я получаю следующий результат:

  • 1 поток: ~ 3% времени выполнения находится внутри malloc/free (~ 3/~ 2)
  • 24 потока: ~ 64% времени выполнения находится внутри malloc/free (~ 33%/~ 31%)

Код сложный, и я не могу опубликовать пример, но в основном это код monte carlo, ther e - небольшое количество динамических распределений (фаза инициализации создает все необходимые данные), остается только одно динамическое распределение в начале одного события для хранения данных события. Код не содержит мьютексов, каждый поток работает без какой-либо связи, кроме как в начале и в конце вычисления.

Мои знания в архитектуре сервера и двух процессоров очень ограничены, и я хочу знать, есть ли что-то, что я могу сделать, чтобы избежать этой проблемы (опция BIOS?), Я предполагаю, что есть контроллер, который выбирает, какая ОЗУ ЦП будет и эта операция замедляется ...

Благодарим вас за чтение.

EDIT: Я написал небольшой тест, чтобы оценить снижение производительности таНос/бесплатно, вот код:

#include <omp.h> 
#include <afx.h> 
#include <vector> 
#include <fstream> 
#include <iostream> 
#include <chrono> 

// malloc allocation size tab 
int allocSize[] = 
{ 
    4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072 
}; 

int main() 
{ 
    // number max of thread 
    int nbThreadMax = omp_get_max_threads(); 
    // malloc/free iteration per bench 
    unsigned int nbIteration = 1000000; 

    // Empty res tab 
    std::vector<double> emptyRes(16, 0.); 
    // Duration per thread 
    std::vector<std::vector<double>> avgDuration(nbThreadMax, emptyRes); 

    int nbThread = 1; 
    unsigned int idxt = 0; 
    while (nbThread <= nbThreadMax) 
    { 
     // Current bench result 
     std::vector<std::vector<double>> threadResult(nbThread, emptyRes); 

     std::cout << "Thread : " << nbThread << std::endl; 

     // Create parrallel region 
     #pragma omp parallel num_threads(nbThread) 
     { 
      int nt = omp_get_thread_num(); 

      for (unsigned int i = 0; i < 16; ++i) 
      { 
       int allocationSize = allocSize[i]; 

       std::chrono::time_point<std::chrono::system_clock> start, end; 
       start = std::chrono::system_clock::now(); 
       for (unsigned int j = 0; j < nbIteration; ++j) 
       { 
        void* pData = malloc(allocationSize); 
        free(pData); 
       } 
       end = std::chrono::system_clock::now(); 

       threadResult[nt][i] += std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count()/1000.; 
      } 
     } 

     // Sum 
     for (unsigned int i = 0; i < 16; ++i) 
     { 
      for (unsigned int j = 0; j <= idxt; ++j) 
      { 
       avgDuration[idxt][i] += threadResult[j][i]; 
      } 
      // /!\ Normalize for one thread /!\ 
      avgDuration[idxt][i] /= nbThread; 
     } 

     ++idxt; 
     // Increase thread number (X2) 
     if (nbThread >= nbThreadMax) 
      break; 
     if (nbThread * 2 > nbThreadMax) 
      nbThread = nbThreadMax; 
     else 
      nbThread = nbThread * 2; 
    } 

    // Write results 
    { 
     std::ofstream ofs("resultats.csv"); 
     ofs << "NbThread;"; 
     for (unsigned int i = 0; i < 16; ++i) 
     { 
      ofs << allocSize[i] << ";"; 
     } 
     ofs << std::endl; 

     int nbThread = 1; 
     for (unsigned int n = 0; n < idxt; ++n) 
     { 
      ofs << nbThread << ";"; 
      for (unsigned int i = 0; i < 16; ++i) 
      { 
       ofs << avgDuration[n][i] << ";"; 
      } 
      ofs << std::endl; 
      nbThread = nbThread * 2; 
     } 

     ofs.close(); 
    } 
} 

Вот результат optained на моем сервере: malloc/free duration /thread malloc/free performance factor /thread

Есть ли такой результат, показывающий проблему, или это нормальное снижение производительности?

+0

24 потока на 2 процессора? –

+1

Нитки не являются святым Граалем исполнения. Особенно на машинах NUMA –

+0

да 24 потока на 2 процессора, я не уверен, что у меня 8X8Gb для ОЗУ. Значит, ты имеешь в виду, что я ничего не могу сделать? – user3513887

ответ

2

Параметры BIOS далеки, слишком экзотичны. Самое простое решение - слегка отклониться от стандартного метода C и использовать собственный метод Windows.

Первым испытанием было бы заменить malloc/free на HeapAlloc. Преимущество здесь в том, что HeapAlloc может поддерживать несколько куч, а с HEAP_NO_SERIALIZE каждая из этих куч может быть однопоточной. Это не означает, что вы должны позвонить HeapFree в той же теме. Вы можете позвонить HeapAlloc в рабочий поток, сохранить результат в выделенном блоке памяти, присоединиться к основному потоку (здесь будет барьер памяти), затем в основном потоке собрать все данные из рабочих потоков и вызвать HeapFree из основного потока. Поскольку рабочие потоки больше не существуют, риск сериализации отсутствует.

Второе улучшение (при необходимости) - проверка поддержки NUMA. Лучше всего привязать потоки к процессору и выделить память из 4xGB, подключенных к этому конкретному процессору. Но это намного сложнее.

0

Один из способов в стандартном C11/C++ 11 - создать кеш с одним входом за нитьmymalloc проверьте, может ли одиночная запись кэша удовлетворить запрос (без необходимости блокировки). Если нет, отложите на обычный malloc.

Большая часть интеллекта находится в myfree. Если уже есть запись в кэш, вам нужно решить, что делать: сохранить самую старую, сохранить самую новую, сохранить наименьшую, сохранить самую большую или потенциально какую-то другую стратегию. (Если вам нужен размер здесь, mymalloc должен перераспределить на sizeof(size_t) и префикс запрашиваемого размера).

Смежные вопросы