1. C++17: параллельная версия стандартных
алгоритмов
Евгений Крутько
e.s.krutko@gmail.com
Национальный исследовательский центр "Курчатовский институт"
25.02.2017
2. Эволюция многопоточной работы в C++
Нам нужно выполнить работу асинхронно
1 int main(){
2 //Here we should use some features to call doWork in parallel
3 doWork(int a);
4 doSomeOtherWork(double t);
5 //Ensure doWork completed
6 return 0;
7 }
3. Давным-давно...
... у нас не было даже стандарта C++11.
1 //Using POSIX API
2 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 }
4. Давным-давно...
... у нас не было даже стандарта C++11.
1 //Using Win API
2 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 NULL
16 );
17 doSomeOtherWork(double t);
18 WaitForSingleObject(thread, INFINITE);
19 return 0;
20 }
6. Начиная со стандарта C++11
std::thread
std::async / std::future
1 int main(int argc, char**argv) {
2 std::thread thread;
3 int value;
4 //Do doWork() in new thread
5 thread = std::thread(
6 doWork,
7 std::ref(value));
8 //Do someything else in this thread
9 doSomeOtherWork();
10 //Whait for doWork() finishes
11 thread.join();
12 return 0;
13 }
7. Начиная со стандарта C++11
std::thread
std::async / std::future
1 int main(int argc, char**argv) {
2 int value;
3 //Do doWork() in new thread
4 auto future = std::async(
5 std::launch::async;
6 doWork,
7 std::ref(value));
8 //Do someything else in this thread
9 doSomeOtherWork();
10 //Whait for doWork() finishes
11 future.get();
12 return 0;
13 }
8. Нам и этого мало, хочется еще проще
Возможно когда-нибудь :)
1 auto auto(auto auto) { auto; }
9. Нам и этого мало, хочется еще проще
Возможно когда-нибудь :)
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 policy
7 std::some_standard_algorythm_with_stl_containers(
8 ExecutionPolicy policy,
9 std::begin(container),
10 std::end(container)
11 );
11. Политики выполнения
1 //Available from <execution_policy>
2 //While in TS stage from <experimental/execution_policy>
3
4 //Plain old sequenced execution
5 constexpr sequential_execution_policy seq{ };
6 //Parallel execution
7 constexpr parallel_execution_policy par{ };
8 //Parallel with SIMD instructions
9 constexpr parallel_vector_execution_policy par_vec{ };
12. Политики выполнения
1 //Available from <execution_policy>
2 //While in TS stage from <experimental/execution_policy>
3
4 //Plain old sequenced execution
5 constexpr sequential_execution_policy seq{ };
6 //Parallel execution
7 constexpr parallel_execution_policy par{ };
8 //Parallel with SIMD instructions
9 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 race
8 });
13. Дайте две!
Как же попробовать? Из документа
https://isocpp.org/files/papers/P0024R2.html
Microsoft MS ParallelSTL page
HPX HPX github
Codeplay Sycl github
HSA HSA for math science page
Thibaut Lutz github
NVIDIA github
http://github.com/eskrut/ParallelSTL.git
14. Тестовая задача #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");
15. Тестовая задача #1
С последовательной политикой
1 //This includes should be from ${T_LUTZ_ROOT}/include
2 #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");
16. Тестовая задача #1
С параллельной политикой
1 //This includes should be from ${T_LUTZ_ROOT}/include
2 #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");
24. Результаты
[2 физических, 4 h-threading ядра]
Тест #1
Реализация Политика Ускорение
t-lutz
seq 1
par 2.37
HPX
seq 0.99
par 2.61
par_vec 2.64
Тест #2
Реализация Политика for Политика sort Ускорение
t-lutz
seq par 2.26
par seq 2.78
par par 2.48
HPX
seq par 2.52
par seq 2.75
par par 2.87
27. И победителем стал...
[2 физических, 4 h-threading ядра]
Тест #2
Реализация Политика Ускорение Количество строк
Parallel STL par/par ∼2.5 2
mine async/seq ∼2.5 20
31. Ловим исключения
Plain for
`o_´o from 0
Seq for
Exception list what: HPX(unknown_error)
Total 1 exceptions:
what: HPX(unknown_error)
Par for
Exception list what: HPX(unknown_error)
Total 13 exceptions:
what: `o_´o from 0
what: `o_´o from 2
what: `o_´o from 4
what: `o_´o from 6
what: `o_´o from 8
what: `o_´o from 10
what: `o_´o from 12
what: `o_´o from 14
what: `o_´o from 16
what: `o_´o from 18
what: `o_´o from 20
what: `o_´o from 22
what: `o_´o from 24
32. На десерт
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, то...
33. На десерт
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 enabled
2
3 #include <parallel/algorithm>
4
5 std::__parallel::some_standard_algorythm_with_stl_containers(
6 std::begin(container),
7 std::end(container)
8 );
34. Те же самые примеры
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 parallel
9 {
10 #pragma omp single
11 {
12 for(auto it = list.begin(); it != list.end(); ++it)
13 #pragma omp task
14 sort(it->begin(), it->end());
15 }
16 }
35. Результаты
[2 физических, 4 h-threading ядра]
Тест #2
Реализация Политика Ускорение Количество строк
Parallel STL par/par ∼2.5 2
mine (std::async) async/seq ∼2.5 20
Parallel gcc par/par ∼2.5 2
mine (openmp) task/seq ∼2.5 9