Parallel STL

35
C++17: параллельная версия стандартных алгоритмов Евгений Крутько [email protected] Национальный исследовательский центр "Курчатовский институт" 25.02.2017

Transcript of Parallel STL

Page 1: Parallel STL

C++17: параллельная версия стандартныхалгоритмов

Евгений Крутько[email protected]

Национальный исследовательский центр "Курчатовский институт"

25.02.2017

Page 2: Parallel STL

Эволюция многопоточной работы в C++Нам нужно выполнить работу асинхронно

1 int main(){2 //Here we should use some features to call doWork in parallel3 doWork(int a);4 doSomeOtherWork(double t);5 //Ensure doWork completed6 return 0;7 }

Page 3: Parallel STL

Давным-давно...... у нас не было даже стандарта C++11.

1 //Using POSIX API2 void* makeDoWork(void *arg) {3 int *a=reinterpret_cast<int*>(arg);4 doWork(*a);5 }6 int main(){7 pthread_t thread;8 int value = 42;9 pthread_create(

10 &thread,11 nullptr,12 makeDoWork,13 &value);14 doSomeOtherWork(double t);15 pthread_join(thread, nullptr);16 return 0;17 }

Page 4: Parallel STL

Давным-давно...... у нас не было даже стандарта C++11.

1 //Using Win API2 DWORD WINAPI makeDoWork(void *arg) {3 int *a=reinterpret_cast<int*>(arg);4 doWork(*a);5 }6 int main(){7 HANDLE thread;8 int value = 42;\\9 thread = CreateThread(

10 NULL,11 0,12 makeDoWork,13 &value,14 0,15 NULL16 );17 doSomeOtherWork(double t);18 WaitForSingleObject(thread, INFINITE);19 return 0;20 }

Page 5: Parallel STL

Кросс-платформенный подход

И другие

Page 6: Parallel STL

Начиная со стандарта C++11std::threadstd::async / std::future

1 int main(int argc, char**argv) {2 std::thread thread;3 int value;4 //Do doWork() in new thread5 thread = std::thread(6 doWork,7 std::ref(value));8 //Do someything else in this thread9 doSomeOtherWork();

10 //Whait for doWork() finishes11 thread.join();12 return 0;13 }

Page 7: Parallel STL

Начиная со стандарта C++11std::threadstd::async / std::future

1 int main(int argc, char**argv) {2 int value;3 //Do doWork() in new thread4 auto future = std::async(5 std::launch::async;6 doWork,7 std::ref(value));8 //Do someything else in this thread9 doSomeOtherWork();

10 //Whait for doWork() finishes11 future.get();12 return 0;13 }

Page 8: Parallel STL

Нам и этого мало, хочется еще прощеВозможно когда-нибудь :)

1 auto auto(auto auto) { auto; }

Уже /почти/ в стандарте

1 //Something from <algorythm>2 std::some_standard_algorythm_with_stl_containers(3 std::begin(container),4 std::end(container)5 );6 //The same but with specification of execution policy7 std::some_standard_algorythm_with_stl_containers(8 ExecutionPolicy policy,9 std::begin(container),

10 std::end(container)11 );

Page 9: Parallel STL

Нам и этого мало, хочется еще прощеВозможно когда-нибудь :)

1 auto auto(auto auto) { auto; }

Уже /почти/ в стандарте

1 //Something from <algorythm>2 std::some_standard_algorythm_with_stl_containers(3 std::begin(container),4 std::end(container)5 );6 //The same but with specification of execution policy7 std::some_standard_algorythm_with_stl_containers(8 ExecutionPolicy policy,9 std::begin(container),

10 std::end(container)11 );

Page 10: Parallel STL

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

Объявлены в заголовочном файле <algorithm>Пока в стадии TS в заголовочном файле<experimental/algorithm>

adjacent_difference adjacent_find all_of any_ofcopy copy_if copy_n countcount_if equal exclusive_scan fillfill_n find find_end find_first_offind_if find_if_not for_each for_each_ngenerate generate_n includes inclusive_scaninner_product inplace_merge is_heap is_heap_untilis_partitioned is_sorted is_sorted_until lexicographical_comparemax_element merge min_element minmax_elementmismatch move none_of nth_elementpartial_sort partial_sort_copy partition partition_copyreduce remove remove_copy remove_copy_ifremove_if replace replace_copy replace_copy_ifreplace_if reverse reverse_copy rotaterotate_copy search search_n set_differenceset_intersection set_symmetric_difference set_union sortstable_partition stable_sort swap_ranges transformtransform_exclusive_scan transform_inclusive_scan transform_reduce uninitialized_copyuninitialized_copy_n uninitialized_fill uninitialized_fill_n uniqueunique_copy

Page 11: Parallel STL

Политики выполнения1 //Available from <execution_policy>2 //While in TS stage from <experimental/execution_policy>3

4 //Plain old sequenced execution5 constexpr sequential_execution_policy seq{ };6 //Parallel execution7 constexpr parallel_execution_policy par{ };8 //Parallel with SIMD instructions9 constexpr parallel_vector_execution_policy par_vec{ };

Стрелять себе по ногам теперь можно еще одним изящнымспособом

1 int a[] = {0,1};2 std::vector<int> v;3 std::for_each(std::par,4 std::begin(a),5 std::end(a),6 [&](int i) {7 v.push_back(i*2+1); // Error: data race8 });

Page 12: Parallel STL

Политики выполнения1 //Available from <execution_policy>2 //While in TS stage from <experimental/execution_policy>3

4 //Plain old sequenced execution5 constexpr sequential_execution_policy seq{ };6 //Parallel execution7 constexpr parallel_execution_policy par{ };8 //Parallel with SIMD instructions9 constexpr parallel_vector_execution_policy par_vec{ };

Стрелять себе по ногам теперь можно еще одним изящнымспособом

1 int a[] = {0,1};2 std::vector<int> v;3 std::for_each(std::par,4 std::begin(a),5 std::end(a),6 [&](int i) {7 v.push_back(i*2+1); // Error: data race8 });

Page 13: Parallel STL

Дайте две!Как же попробовать? Из документаhttps://isocpp.org/files/papers/P0024R2.html

Microsoft MS ParallelSTL pageHPX HPX githubCodeplay Sycl githubHSA HSA for math science pageThibaut Lutz githubNVIDIA github

http://github.com/eskrut/ParallelSTL.git

Page 14: Parallel STL

Тестовая задача #1Обычная реализация

1 auto vec = makeShuffledVector();2 double baseDuration = 0;3

4 auto vecToSort = vec;5 {6 Stopwatch sw("plain sort");7 std::sort(std::begin(vecToSort), std::end(vecToSort));8 baseDuration = sw.duration();9 }

10 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))11 throw std::runtime_error("Failed with plain sort");

Page 15: Parallel STL

Тестовая задача #1С последовательной политикой

1 //This includes should be from ${T_LUTZ_ROOT}/include2 #include <numeric>3 #include <experimental/algorithm>4

5 /* --- */6

7 vecToSort = vec;8 {9 Stopwatch sw("seq sort", baseDuration);

10 sort(std::experimental::parallel::seq,11 std::begin(vecToSort), std::end(vecToSort));12 }13 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))14 throw std::runtime_error("Failed with plain sort");

Page 16: Parallel STL

Тестовая задача #1С параллельной политикой

1 //This includes should be from ${T_LUTZ_ROOT}/include2 #include <numeric>3 #include <experimental/algorithm>4

5 /* --- */6

7 vecToSort = vec;8 {9 Stopwatch sw("par sort", baseDuration);

10 sort(std::experimental::parallel::par,11 std::begin(vecToSort), std::end(vecToSort));12 }13 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))14 throw std::runtime_error("Failed with plain sort");

Page 17: Parallel STL

Тестовая задача #1С последовательной политикой

1 #include "hpx/hpx_init.hpp"2 #include "hpx/hpx.hpp"3

4 #include "hpx/parallel/numeric.hpp"5 #include "hpx/parallel/algorithm.hpp"6

7 /* --- */8

9 vecToSort = vec;10 {11 Stopwatch sw("seq sort", baseDuration);12 hpx::parallel::sort(hpx::parallel::seq,13 std::begin(vecToSort), std::end(vecToSort));14 }15 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))16 throw std::runtime_error("Failed with seq sort");

Page 18: Parallel STL

Тестовая задача #1С параллельной политикой

1 #include "hpx/hpx_init.hpp"2 #include "hpx/hpx.hpp"3

4 #include "hpx/parallel/numeric.hpp"5 #include "hpx/parallel/algorithm.hpp"6

7 /* --- */8

9 vecToSort = vec;10 {11 Stopwatch sw("par sort", baseDuration);12 hpx::parallel::sort(hpx::parallel::par,13 std::begin(vecToSort), std::end(vecToSort));14 }15 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))16 throw std::runtime_error("Failed with par sort");

Page 19: Parallel STL

Тестовая задача #1С параллельной и векторизованной политикой

1 #include "hpx/hpx_init.hpp"2 #include "hpx/hpx.hpp"3

4 #include "hpx/parallel/numeric.hpp"5 #include "hpx/parallel/algorithm.hpp"6

7 /* --- */8

9 vecToSort = vec;10 {11 Stopwatch sw("par_vec sort", baseDuration);12 hpx::parallel::sort(hpx::parallel::par_vec,13 std::begin(vecToSort), std::end(vecToSort));14 }15 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))16 throw std::runtime_error("Failed with par_vec sort");

Page 20: Parallel STL

Тестовая задача #2Обычная реализация

1 const size_t numParts = std::thread::hardware_concurrency()*2 > 0 ?2 std::thread::hardware_concurrency() * 2 : 8;3 std::list<std::vector<size_t>> listOfVecs;4 listOfVecs.resize(numParts);5 std::generate(listOfVecs.begin(),6 listOfVecs.end(),7 [&](){ return makeShuffledVector(memoryToAlloc/numParts);}8 );9 double baseDuration = 0;

10 auto list = listOfVecs;11 {12 Stopwatch sw("plain for, plain sort");13 for(auto &vecToSort : list)14 std::sort(std::begin(vecToSort), std::end(vecToSort));15 baseDuration = sw.duration();16 }17 for(const auto &vecToSort : list)18 if (! std::is_sorted(std::begin(vecToSort), std::end(vecToSort)))19 throw std::runtime_error("Failed with plain for, plain sort");

Page 21: Parallel STL

Тестовая задача #2Последовательный for, параллельный sort

1 for(auto &vecToSort : list)2 sort(std::experimental::parallel::par,3 std::begin(vecToSort), std::end(vecToSort));4

5 /* --- */6

7 for(auto &vecToSort : list)8 hpx::parallel::sort(hpx::parallel::par,9 std::begin(vecToSort),

10 std::end(vecToSort));

Page 22: Parallel STL

Тестовая задача #2Параллельный for, последовательный sort

1 for_each(std::experimental::parallel::par,2 std::begin(list), std::end(list),3 [](std::vector<size_t> &vecToSort){4 std::sort(std::begin(vecToSort), std::end(vecToSort));5 });6

7 /* --- */8

9 hpx::parallel::for_each(hpx::parallel::par,10 std::begin(list), std::end(list),11 [](std::vector<size_t> &vecToSort){12 std::sort(std::begin(vecToSort), std::end(vecToSort));13 });

Page 23: Parallel STL

Тестовая задача #2Параллельный for, параллельный sort[параллельный в квадрате, квадратно параллельный]

1 for_each(std::experimental::parallel::par,2 std::begin(list), std::end(list),3 [](std::vector<size_t> &vecToSort){4 sort(std::experimental::parallel::par,5 std::begin(vecToSort), std::end(vecToSort));6 });7

8 /* --- */9

10 hpx::parallel::for_each(hpx::parallel::par,11 std::begin(list), std::end(list),12 [](std::vector<size_t> &vecToSort){13 hpx::parallel::sort(hpx::parallel::par,14 std::begin(vecToSort), std::end(vecToSort));15 });

Page 24: Parallel STL

Результаты

[2 физических, 4 h-threading ядра]Тест #1

Реализация Политика Ускорение

t-lutzseq 1par 2.37

HPXseq 0.99par 2.61

par_vec 2.64

Тест #2Реализация Политика for Политика sort Ускорение

t-lutzseq par 2.26par seq 2.78par par 2.48

HPXseq par 2.52par seq 2.75par par 2.87

Page 25: Parallel STL

Race!Догнать и перегнать!

1 for(auto &vecToSort : list)2 std::sort(std::begin(vecToSort), std::end(vecToSort));3

4 /* --- */5

6 for_each(std::experimental::parallel::par,7 std::begin(list), std::end(list),8 [](std::vector<size_t> &vecToSort){9 sort(std::experimental::parallel::par,

10 std::begin(vecToSort), std::end(vecToSort));

Page 26: Parallel STL

Race!1 auto work=[](decltype(list.end()) begin,decltype(list.end()) end){2 for(auto it = begin; it != end; ++it)3 std::sort(it->begin(), it->end());4 };5 size_t hc = std::thread::hardware_concurrency();6 if(hc == 0) hc = 8;7 auto numThreads = std::min(list.size(), hc);8 auto chunkPerThread = list.size() / numThreads;9 auto threadBegin = list.begin();

10 auto threadEnd = threadBegin;11 std::advance(threadEnd, chunkPerThread);12 std::list<std::future<void>> futures;13 for(size_t thId = 0; thId < numThreads - 1; ++thId){14 futures.push_back(std::async(std::launch::async,15 work, threadBegin, threadEnd));16 threadBegin = threadEnd;17 std::advance(threadEnd, chunkPerThread);18 }19 work(threadBegin, list.end());20 for(auto &f : futures) f.get();

Page 27: Parallel STL

И победителем стал...

[2 физических, 4 h-threading ядра]

Тест #2Реализация Политика Ускорение Количество строкParallel STL par/par ∼2.5 2

mine async/seq ∼2.5 20

Page 28: Parallel STL

Динамические политики1 std::experimental::parallel::execution_policy outerPolicy =2 std::experimental::parallel::seq;3 std::experimental::parallel::execution_policy innerPolicy =4 std::experimental::parallel::par;5

6 /* Decide at runtime which policy should be */7 outerPolicy = std::experimental::parallel::par;8 innerPolicy = std::experimental::parallel::par_vec;9

10 for_each(outerPolicy,11 std::begin(list), std::end(list),12 [&innerPolicy](std::vector<size_t> &vecToSort){13 sort(innerPolicy,14 std::begin(vecToSort),15 std::end(vecToSort));16 });

Page 29: Parallel STL

Если что-то пошло не так1 struct Processor2 {3 void doWork(size_t id) {4 throw std::runtime_error("oo_ from " + std::to_string(id));5 }6 };7

8 std::vector<Processor> processors;9 processors.resize(25);

10

11 try {12 std::cout << "Plain for" << std::endl;13 std::for_each(processors.begin(), processors.end(),14 [&](Processor &p){15 p.doWork(&p - processors.data());16 });17 }18 catch(std::exception &e) {19 std::cout << e.what() << std::endl;20 }

Page 30: Parallel STL

Ловим исключения1 try {2 std::cout << "Seq for" << std::endl;3 hpx::parallel::for_each(hpx::parallel::seq,4 processors.begin(), processors.end(),5 [&](Processor &p){6 p.doWork(&p - processors.data());7 });8 }9 catch(hpx::parallel::exception_list &list) {

10 std::cout << "Exception list what: " << list.what() << std::endl;11 std::cout << "Total " << list.size() << " exceptions:" << std::

endl;12 for(auto &e : list) {13 try{ boost::rethrow_exception(e); }14 catch(std::exception &e){15 std::cout << "\twhat: " << e.what() << std::endl;16 }17 }18 }19 catch(std::exception &e){20 std::cout << e.what() << std::endl;21 }

Page 31: Parallel STL

Ловим исключенияPlain foro_o from 0Seq forException list what: HPX(unknown_error)Total 1 exceptions:

what: HPX(unknown_error)Par forException list what: HPX(unknown_error)Total 13 exceptions:

what: o_o from 0what: o_o from 2what: o_o from 4what: o_o from 6what: o_o from 8what: o_o from 10what: o_o from 12what: o_o from 14what: o_o from 16what: o_o from 18what: o_o from 20what: o_o from 22what: o_o from 24

Page 32: Parallel STL

На десертParallelism Extension for STL еще не реализовано вкомпиляторах.

1 #include <algorithm>2

3 std::some_standard_algorythm_with_stl_containers(4 std::begin(container),5 std::end(container)6 );

Но если используется gcc, то...

1 //OpenMP should be enabled2

3 #include <parallel/algorithm>4

5 std::__parallel::some_standard_algorythm_with_stl_containers(6 std::begin(container),7 std::end(container)8 );

Page 33: Parallel STL

На десертParallelism Extension for STL еще не реализовано вкомпиляторах.

1 #include <algorithm>2

3 std::some_standard_algorythm_with_stl_containers(4 std::begin(container),5 std::end(container)6 );

Но если используется gcc, то...

1 //OpenMP should be enabled2

3 #include <parallel/algorithm>4

5 std::__parallel::some_standard_algorythm_with_stl_containers(6 std::begin(container),7 std::end(container)8 );

Page 34: Parallel STL

Те же самые примеры1 std::__parallel::sort(std::begin(vecToSort), std::end(vecToSort));2

3 std::__parallel::for_each(std::begin(list), std::end(list),4 [](std::vector<size_t> &vecToSort){5 std::__parallel::sort(std::begin(vecToSort), std::end(

vecToSort));6 });7

8 #pragma omp parallel9 {

10 #pragma omp single11 {12 for(auto it = list.begin(); it != list.end(); ++it)13 #pragma omp task14 sort(it->begin(), it->end());15 }16 }

Page 35: Parallel STL

Результаты

[2 физических, 4 h-threading ядра]

Тест #2Реализация Политика Ускорение Количество строкParallel STL par/par ∼2.5 2

mine (std::async) async/seq ∼2.5 20Parallel gcc par/par ∼2.5 2

mine (openmp) task/seq ∼2.5 9