SMP

May. 11th, 2017 08:16 pm
thedeemon: (bednota)
[personal profile] thedeemon
А вот еще случай был. Программа генерит длинную последовательность неких значений (phase_1), и поблочно эти значения потом как-то дальше обрабатываются (phase_2), производя добро (PROFIT!). И надо бы это делать побыстрее. Пришла очевидная мысль это дело распараллелить: пока phase_2 обрабатывает первый блок данных от phase_1, в это время phase_1 может уже параллельно создавать следующий блок, и так далее, блок N обрабатывается параллельно созданию блока N+1. В каждый момент времени в памяти лишь два блока - генерируемый и обрабатываемый. Завел для них два массива: то пишем в первый, читаем из второго, то наоборот.
std::vector<Freq> ranges[2];

При этом phase_1 что-то пишет в один из массивов через push_back, а обработка блока в phase_2 выглядела как-то так:
BYTE* writeBlock(const std::vector<Freq> &ranges, BYTE *dst) {
    ...
    for(int i=ranges.size()-1; i>=0; i--) {
        ... // use ranges[i]
    }    
    ...
}

До распараллеливания phase_1 занимал по времени 12 попугаев, phase_2 - 13 попугаев, всего 25. Запускаю новый распараллеленный вариант и вдруг вижу, что общее время стало аж 41 попугай. Вместо ускорения получил замедление!
А теперь беру и меняю пару строк:
BYTE* writeBlockV(const std::vector<Freq> &rangesV, BYTE *dst) {
    ...
    const Freq * ranges = &rangesV[0];
    for(int i=rangesV.size()-1; i>=0; i--) {
        ... // use ranges[i]
    }
    ...
}

И общее время становится менее 15 попугаев, почти втрое быстрее! Т.е. в первом случае код в цикле каждый раз загружал указатель на данные в векторе, а этот указатель лежал в памяти рядом с длиной другого массива, которая менялась в другом потоке, так что два ядра тягали этот кусочек памяти туда-сюда: одно ядро туда пишет новую длину, второе ядро копирует изменившуюся кэш-линию, чтобы прочитать указатель по соседству. Из-за этих тяганий туда-сюда получается медленнее, чем если все делать в один поток. А стоило указатель на данные вектора унести в стек и перестать читать меняющуюся кэш-линию, как все заработало быстро, как и планировалось.
Причем безобразие такое только у интеловского компилятора. MSVC и GCC не перечитывают указатель и тяганий кэш-линий не устраивают, оба варианта кода работают с одинаковой скоростью. При этом с GCC время получается чуть менее 16 попугаев, а с MSVC - почти 33 (что все же быстрее непараллеленного варианта, где у MSVC 42 попугая).
From:
Anonymous( )Anonymous This account has disabled anonymous posting.
OpenID( )OpenID You can comment on this post while signed in with an account from many other sites, once you have confirmed your email address. Sign in using OpenID.
User
Account name:
Password:
If you don't have an account you can create one now.
Subject:
HTML doesn't work in the subject.

Message:

 
Notice: This account is set to log the IP addresses of everyone who comments.
Links will be displayed as unclickable URLs to help prevent spam.

Profile

thedeemon: (Default)
Dmitry Popov

September 2017

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 23rd, 2017 09:16 am
Powered by Dreamwidth Studios