Многопоточность: почему две программы лучше, чем одна?

Коротко о моей проблеме:

У меня есть компьютер с 2 разъемами AMD Opteron 6272 и 64 ГБ оперативной памяти.

Я запускаю одну многопоточную программу на всех 32 ядрах и получаю скорость на 15% меньше по сравнению со случаем, когда я запускаю 2 программы, каждая на одном 16-ядерном сокете.

Как сделать однопрограммную версию такой же быстрой, как две?


Больше деталей:

У меня большое количество задач и я хочу полностью загрузить все 32 ядра системы.
Поэтому я упаковываю задачи в группы по 1000 единиц. Для такой группы требуется около 120 МБ входных данных, и на одном ядре требуется около 10 секунд. Чтобы сделать тест идеальным, я копирую эти группы 32 раза и использую ITBB parallel_for Цикл распределяет задачи между 32 ядрами.

я использую pthread_setaffinity_np чтобы эта система не заставляла мои потоки прыгать между ядрами. И чтобы все ядра использовались последовательно.

я использую mlockall(MCL_FUTURE) чтобы эта система не заставляла мою память перепрыгивать между сокетами.

Итак, код выглядит так:

  void operator()(const blocked_range<size_t> &range) const
{
for(unsigned int i = range.begin(); i != range.end(); ++i){

pthread_t I = pthread_self();
int s;
cpu_set_t cpuset;
pthread_t thread = I;
CPU_ZERO(&cpuset);
CPU_SET(threadNumberToCpuMap[i], &cpuset);
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated

TaskManager manager;
for (int j = 0; j < fNTasksPerThr; j++){
manager.SetData( &(InpData->fInput[j]) );
manager.Run();
}
}
}

Мне важно только вычислительное время, поэтому я подготавливаю входные данные в отдельные parallel_for петля. И не включайте время подготовки в измерения времени.

  void operator()(const blocked_range<size_t> &range) const
{
for(unsigned int i = range.begin(); i != range.end(); ++i){

pthread_t I = pthread_self();
int s;
cpu_set_t cpuset;
pthread_t thread = I;
CPU_ZERO(&cpuset);
CPU_SET(threadNumberToCpuMap[i], &cpuset);
s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset);

mlockall(MCL_FUTURE); // lock virtual memory to stay at physical address where it was allocated
InpData[i].fInput = new ProgramInputData[fNTasksPerThr];

for(int j=0; j<fNTasksPerThr; j++){
InpData[i].fInput[j] = InpDataPerThread.fInput[j];
}
}
}

Теперь я запускаю все это на 32 ядрах и вижу скорость ~ 1600 задач в секунду.

Затем я создаю две версии программы, и с taskset а также pthread убедитесь, что первый работает на 16 ядрах первого сокета, а второй — на втором сокете. Я запускаю их один рядом друг с другом, используя просто & команда в оболочке:

program1 & program2 &

Каждая из этих программ достигает скорости ~ 900 задач / с. В общей сложности это> 1800 задач / с, что на 15% больше, чем в одной программе.

Что мне не хватает?

Я считаю, что это может быть проблема в библиотеках, которые я загружаю в память только для проверки потока. Может ли это быть проблемой? Можно ли скопировать данные библиотек, чтобы они были доступны независимо на обоих сокетах?

12

Решение

Я предполагаю, что это распределение памяти STL / boost, которое распределяет память для ваших коллекций и т. Д. По узлам numa, потому что они не осведомлены о numa, и у вас есть потоки в программе, работающей на каждом узле.

Пользовательские распределители для всех вещей STL / boost, которые вы используете, могут помочь (но, вероятно, это огромная работа).

3

Другие решения

Возможно, вы страдаете из-за ложного совместного использования кэша: http://en.wikipedia.org/wiki/False_sharing

Ваши потоки, вероятно, совместно используют доступ к той же структуре данных через ссылку block_range. Если вам нужна скорость, вы можете передать копию каждому потоку. Если ваши данные слишком велики, чтобы поместиться в стек вызовов, вы можете динамически распределить копию каждого диапазона в разных сегментах кэша (т.е. просто убедитесь, что они достаточно далеко расположены).

Или, может быть, мне нужно увидеть остальную часть кода, чтобы понять, что вы делаете лучше.

1

По вопросам рекламы [email protected]