Visual C++ã®C++17対å¿
ã並åã¢ã«ã´ãªãºã ï¼parallel algoritmsã¯C++17ã§æ¨æºC++ã«çµã¿å ¥ãããã¾ããã4å¹´ã»ã©åãparallel algorithmsã®Microsoftå®è£ ï¼ParallelSTLãç´¹ä»ãã¾ããããããVisual C++ã§å ¬å¼ãµãã¼ããããããããªããã¨æå¾ ãã¦ãããã©ãVisual C++ 2017ãªãªã¼ã¹æã«ã¯å ¥ã£ã¦ãªãã¦ãã£ãããããã§ãããã
ãã§ãä»å¹´5æã®Visual Studio 2017 updateã§ãµãã¼ãããã¨ã®æ å ±ãè³ã«ãã¦æ©éæ´æ°ãããã¨ãããVisual Studioã®ãã¼ã¸ã§ã³ã¯15.7.xãVisual C++ã¯MSCãã¼ã¸ã§ã³19.14ã«updateããã¾ããã
ãVisual C++éçºãã¼ã ã®blogã«ããã¨ãVisual C++ã¯ä»åã®updateã§C++17対å¿ãããããå®äºããããã§ããããã¾ã§g++ãclangã«å¾ããåã£ã¦ããã®ããããã追ãã¤ãã¦åã°ããéããC++17ã¯ã³ã³ãã¤ã©ã®ã³ãã³ãã©ã¤ã³ã»ãªãã·ã§ã³ããã³ããã¸ã§ã¯ãã»ããããã£ã«-std:c++17ãããã¯-std:c++latestã®æå®ããå¿ããªããããã©ã«ãã§ã¯C++14ï¼-std:c++14ï¼æ±ããããã§ãã
parallel algorithmsãããã
ãparallel algorithmsã«ãã並ååã®å¹æã確èªãã¹ããããããã®è¨ç®æéãè¦ããå¦çï¼ä¸ããèªç¶æ°ãç´ æ°ãå¦ããå¤å®ããclass prime_judgeãç¨æãã¾ããã
#pragma once #include <algorithm> #include <vector> #include <stdexcept> class prime_judge { private: std::vector<int> primes_; public: prime_judge(int N) { primes_.push_back(2); // å¥æ° 3, 5, 7 ... ã«å¯¾ã for ( int n = 3; n <= N; n += 2 ) { // primes_ã®ä¸ã« n ãå²ãåãæ°ããªããã° if ( std::none_of(primes_.begin(), primes_.end(), [n](int p) { return n%p == 0;}) ) { // nã¯ç´ æ°ã ããprimes_ã«è¿½å ãã primes_.push_back(n); } } } // nãç´ æ°ãªãtrueãè¿ã bool operator()(int n) const { if ( n > primes_.back()*primes_.back() ) { throw std::domain_error("too big to determine."); } if ( n < 2 ) return false; return binary_search(primes_.begin(), primes_.end(), n) || std::none_of(primes_.begin(), primes_.end(), [n](int p) { return n%p == 0;}); } };
ãã³ã³ã¹ãã©ã¯ã¿ã«ä¸ããNã«ãããNããå°ããªç´ æ°ãæé ã«ä¸¦ã¹ãç´ æ°è¡¨ãä½ã£ã¦ããã¾ããint nãç´ æ°ãªãtrueãè¿ãã¡ã³ãé¢æ°ï¼bool operator()(int n) constã§ã¯ãnãç´ æ°è¡¨ã«å«ã¾ãã¦ããã°trueããããªãã°nãå²ãåãç´ æ°ãç´ æ°è¡¨å ã«è¦ã¤ãããªããã°trueãè¿ãã¾ãããã®æ¹æ³ã§ãã³ã³ã¹ãã©ã¯ã¿ã«ä¸ããNã«å¯¾ãN*Næªæºã®nã«ã¤ãã¦ç´ æ°ãå¦ããå¤å®ã§ãã¾ãã
ãprime_judgeã¨std::count_ifã使ã£ã¦10ä¸ä»¥ä¸ã®ç´ æ°ãããã¤ãããåå®ãã¦ã¿ã¾ããã
#include "prime_judge.h" #include <iostream> #include <numeric> #include <algorithm> template<typename Iterator> size_t count_seq(Iterator first, Iterator last, const prime_judge& judge) { return std::count_if(first, last, judge); } int main() { using namespace std; prime_judge judge(1000); vector<int> data(100000); iota(data.begin(), data.end(), 1); // data ã 1, 2 ... 99999 ã§åãã size_t count = count_seq(data.begin(), data.end(), judge); cout << count << " primes found.\n"; }
ã9592åã®ç´ æ°ãè¦ã¤ãã¾ãããã
ãããã§ã¯ããã¤ã並ååãã¾ããæè¦æéããã¤ã¯ãç§åä½ã§è¨æ¸¬ããé¢æ°ãç¨æããèªåã§ã¹ã¬ããåå²ãããã®ãmicrosoft PPLï¼Parallel Patterns Libraryï¼ã§ã®å®è£ ãããã¦C++17 parallel algorithmsã«ããå®è£ ã®3æ¬ã§ããããã®æè¦æéãã·ã³ã°ã«ã¹ã¬ããã®é 次å®è¡çã¨æ¯ã¹ãã¹ãã¼ãã¢ããçãæ¯ã¹ã¾ãã
#pragma once #include <chrono> template<typename Function> long long measure(Function fun, int repeat =1) { using namespace std::chrono; auto start = high_resolution_clock::now(); while ( repeat-- ) fun(); auto stop = high_resolution_clock::now(); return duration_cast<microseconds>(stop - start).count(); }
#include "prime_judge.h" #include "measure.h" #include <iostream> #include <iomanip> #include <numeric> #include <algorithm> /* * é 次å¦ç */ template<typename Iterator> size_t count_seq(Iterator first, Iterator last, const prime_judge& judge) { return std::count_if(first, last, judge); } /* * èªåã§ã¹ã¬ããåå² */ #include <thread> #include <future> #include <iterator> template<typename Iterator> size_t count_par_byhand(Iterator first, Iterator last, const prime_judge& judge) { // ç©ççã«ä¸¦è¡åä½å¯è½ãªã¹ã¬ããæ° unsigned int num_threads = std::thread::hardware_concurrency(); // 1ã¤ã®ã¹ã¬ãããåãæã¤taskæ° size_t step = std::distance(first, last) / num_threads; std::vector<std::future<size_t>> futures(num_threads); auto count_prime = [&](Iterator f, Iterator l) -> size_t { return std::count_if(f, l, judge); }; Iterator tfirst = first; Iterator tlast; for ( unsigned int i = 0; i < num_threads; ++i ) { tlast = ( i+1 == num_threads ) ? last : std::next(tfirst, step); futures[i] = std::async(count_prime, tfirst, tlast); // ã¹ã¬ããèµ·å tfirst = tlast; } // åã¹ã¬ããã§æ±ããæ°ãç©ç®ãã size_t count = 0; for ( auto& fut : futures ) { count += fut.get(); } return count; } /* * Parallel Patterns Library */ #include <ppl.h> #include <atomic> template<typename Iterator> size_t count_par_ppl(Iterator first, Iterator last, const prime_judge& judge) { std::atomic<size_t> count = 0; concurrency::parallel_for_each(first, last, [&](int n) { // nãç´ æ°ãªãcount+1 if ( judge(n) ) { ++count; } }); return count; } /* * C++17 parallel algorithms */ #include <execution> template<typename Iterator> size_t count_par_cpp17(Iterator first, Iterator last, const prime_judge& judge) { return count_if(std::execution::par, first, last, judge); } /*--------------------------------------------------*/ void run() { using namespace std; prime_judge judge(1000); vector<int> data(100000); iota(data.begin(), data.end(), 1); size_t count; long long par_dur; long long seq_dur; auto percent_change = [](long long o, long long n) { return (o-n)*100/n; }; cout << "sequential: "; seq_dur = measure([&]() { count = count_seq(data.begin(), data.end(), judge); }); cout << count << " primes found in " << seq_dur << "[us]\n"; cout << "parallelism by hand: "; par_dur = measure([&]() { count = count_par_byhand(data.begin(), data.end(), judge); }); cout << count << " primes found in " << par_dur << "[us] " << setw(3) << percent_change(seq_dur, par_dur) << "%\n"; cout << "Parallel Patterns Lib.: "; par_dur = measure([&]() { count = count_par_ppl(data.begin(), data.end(), judge); }); cout << count << " primes found in " << par_dur << "[us] " << setw(3) << percent_change(seq_dur, par_dur) << "%\n"; cout << "c++17 parallel algo.: "; par_dur = measure([&]() { count = count_par_cpp17(data.begin(), data.end(), judge); }); cout << count << " primes found in " << par_dur << "[us] " << setw(3) << percent_change(seq_dur, par_dur) << "%\n"; cout << endl; } int main() { run(); run(); run(); run(); run(); run(); run(); run(); run(); run(); }
ãèªåã§ã¹ã¬ããåå²çï¼std::thread::hardware_concurrency()ãè¿ããã¼ãã¦ã§ã¢ã§ä¸¦è¡åä½å¯è½ãªæ°ãè¦ããã«è«çã³ã¢æ°ã¨åæ°ã®ã¹ã¬ãããèµ·ããã10ä¸ã®ã¿ã¹ã¯ãåçã«åãåãã¦åã¹ã¬ããã«ä¸ããããããã®çµæãæ¾ã£ã¦éè¨ãã¦ãã¾ããasyncï¼future使ã£ã¦æ¥½ãã¦ã¾ãããããã§ãããããã¡ã³ãèãå®è£ ã§ãã
ãPPLçã§ã¯concurrency::parallel_for_each()ã使ãã¾ãããè¤æ°ã®ã¹ã¬ããã1ã¤ã®é åãåæã«ã¢ã¯ã»ã¹ãããã¨ã«ãã£ã¦èµ·ããdeta-raceãé¿ããã¹ããæ°ã®ç©ã¿ä¸ãã«atomic<int>ãå©ç¨ãã¦ãã¾ããèªåã§ã¹ã¬ããåå²ããããããã¶ã楽ã§ã¯ããã¾ãããPPLã¯parallel_count_if()ãªãã¦æ°ã®å©ããé¢æ°ãæã¡åããã¦ããªãã®ã§ããããªå®è£ ã¨ãªãã¾ããã
ãparallel algoritmsçã ã¨ãã£ããªãã»ã©ç°¡åããããï¼<execution>ã«å®ç¾©ãããå®è¡ããªã·ã¼ï¼std::execution::parãcount_if()ã®ç¬¬ä¸å¼æ°ã«ä¸ããã ãã
ããããªå®è¡çµæãå¾ããã¾ããã
ãè«çã³ã¢8åã®i7ã§ã®çµæã§ãã並ååãããã®ã¯ãã©ããé 次å®è¡ã«æ¯ã¹ç´3åã®ã¹ãã¼ãã¢ãããparallel algorithmsã®ãæ軽ããå ã£ã¦ã¾ããâª