2016-01-07 2 views
2

Это мой первый пост здесь, хотя я регулярно посещаю сайт и нахожу здесь много полезной информации.Почему производительность этого неловко параллельного алгоритма не улучшается при многопоточности?

У меня есть смущающий параллельный алгоритм, который, как я ожидал, продемонстрирует большие улучшения производительности при многопоточности.

Это мой первый опыт многопоточности, после небольшого количества чтения и обзора.

Я работаю на C++ с VS 2012, а мой ноутбук с Windows 7 оснащен процессором i7 с четырьмя ядрами и большим количеством памяти.

Основная работа распадается на этот псевдокод

for (int i = 0; i<iMax; i++){ 
    for (int j = 0; j<jMax; j++){ 
     T[j] += E[j][i] * SF; 
    } 
} 

Т, Е и SF являются поплавками.

Реализация использует (измененную) threadpool от here.

и строит и добавляет кучу задач для ThreadPool от этой функции

void doWork(float *T, float *E, float SF, int numNodes) 
{ 
    // Critical for performance that these loops vectorize..... 
    for (int nodeCounter = 0; nodeCounter < numNodes; nodeCounter++){ 
     T[nodeCounter] += E[nodeCounter] * SF; 
    } 
}; 

используя эту конструкцию,

tp.enqueue(std::bind(&doWork, timeStepDisplacements.T1, T1MODE, T1MPF, numNodes)); 

в моих тестах, numNodes является 1000000 и я называю это рутинные 3 раза (с разными массивами) для каждой из 50 внешних петель. У меня есть еще одна петля (100) вокруг нее тоже, поэтому мой тестовый код генерирует этих задач с каждой задачей, выполняющей 1,000,000 умножений.

EDIT: Corrected внешнего счетчика цикла до 100 и числа задач, от 7500 до 15000

Когда я создал мой ThreadPool с 8, 16 или более нитями, производительность лишь незначительно лучше, чем последовательный код - скажем, 8,8 секунды против 9,3.

Итак, мой вопрос в том, почему улучшение производительности настолько мало?

ПРИМЕЧАНИЕ. Если используется другая задача (work_proc ниже), то такая же настройка потока потока показывает отличную прибыль.

void work_proc() 
{ 
    int i = 555; 
    std::random_device rd; 
    std::mt19937 rng(rd()); 

    // build a vector of random numbers 
    std::vector<int> data; 
    data.reserve(100000); 
    std::generate_n(std::back_inserter(data), data.capacity(), [&](){ return rng(); }); 
    std::sort(data.begin(), data.end()); 
} 

У меня нет проблем с отправкой всего кода, но я решил, что начну с этих ключевых элементов.

Thanx заранее для любого понимания, которое будет предложено.

+0

Приращение 'j' во внутреннем цикле может означать много промахов в кэше. Возможно, попробуйте рефакторинг цикла, чтобы он был более дружественным к кешу. –

+0

Поддерживает ли ваша ОС потоки на отдельных ядрах или же ядро? –

+0

Имеет ли в каждом ядре отдельный процессор с плавающей запятой или аппаратное обеспечение? –

ответ

4

Возможно, вы замаскировали некоторые важные биты, но если ваш псевдокод является точным, то это похоже на то, что узким местом является доступ к памяти.

Одно ядро ​​может добавлять числа достаточно быстро, чтобы ваша DRAM использовалась в значительной степени полностью, поэтому получить большую производительность не удалось, разделив эту работу.

EDIT: Вы можете рассчитать скорость передачи DRAM, если знаете свой тип DRAM и тактовую частоту ввода/вывода. Это о том, как быстро это происходит?

Например: 15000 * 1000000 поплавков за 9,3 секунды составляет 6,4 ГБ/с для считывания. Если вы пишете ту же сумму, то это 12,8 ГБ/с, что является максимальной скоростью для DDR3-1600, что вы говорите, что используете в комментариях ...

Так что это, безусловно, ваша проблема.

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

Если у вас есть каждый работник сделать 4 Es, как:

T[nodeCounter] += (E1[nodeCounter] + E2[nodeCounter] + E3[nodeCounter] + E4[nodeCounter])*SF 

то, что приведет к снижению пропускной способности канала T значительно, и вы получите очень близко к максимальной скорости.

+0

Matt - Попытка выяснить мои спецификации оборудования сейчас ,,,,, ЦП i7-4710MQ на 2,5 ГГц. Не очевидно, где я нахожу другую информацию - но я ищу ... – max375

+0

Память DDR3L @ 1600 МГц – max375

+0

Можете ли вы протестировать, используя [nodecounter & 255] вместо [nodecounter]? Это устранит любую проблему с пропускной способностью памяти (хотя это может привести к конфликту, поэтому попробуйте с 1, а затем 2 потоками) –