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

Parallel STL

Mar 03, 2017

Download

Software

Evgeny Krutko
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
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