2016-05-05 15 views
1

複雑な浮動小数点の2つのベクトルを乗算し、その結果を合計するコードを並列化しようとしています。これを行うには、私は先物を使ってstd :: asyncを使用しようとしています。私の考えは、ベクトルを8つの部分に分割し、これら8つの部分のそれぞれを並列に乗算してから最終結果を得ることでした。これを行うには、2つのベクトルを乗算して結果を合計するラムダを含む8つの先物を作成します。それぞれの未来は、この特定の未来が行動すべきベクトルのセクションを表すベクトルの異なる位置へのポインタを渡されます。async C++を使用したベクトルのパラレル乗算

しかし、それは私が期待したスピードアップを与えてくれているようではありません。コードのこのセクションが20-30%スピードアップしているかもしれませんが、それだけでなく負荷も広がっていないようです私のコア(ハイパースレッディング付きの4または8)ではなく、100%の1つのコアにすべてがあるようです。

以下のコードを追加しました。どんな提案も大歓迎です。書かれたよう

size_t size = Input1.size()/8; 

std::vector<std::future<complex<float> > > futures; 
futures.reserve(8); 

for(int i = 0; i<8; ++i) 
{ 
    futures.push_back(std::async([](complex<float>* pos, complex<float>*pos2, size_t siz) 
    { 
     complex<float> resum(0,0); 
     for(int i = 0; i < siz; ++i) 
      resum += pos[i]*pos2[i]; 
     return resum; 
    }, &Input1[i*size], &Input2[i*size], size)); 
} 


complex<float> ResSum(0,0); 
for(int i = 0; i < futures.size(); ++i) 
    ResSum += futures.at(i).get(); 
+0

これらのベクターの大きさはどれくらいですか?境界線の境界線で入力を分割したい場合があります。これはおそらくベクタライズやアルゴリズムの再配置により、キャッシュを最大限に活用することができます。 –

+0

どのくらい役立つか分かりませんが、スレッド内のメモリ割り当てを防ぐために 'resum'で' reserve'呼び出しを行うべきです。 – NathanOliver

+0

これがこの特定の場合のタイミングに影響を与えるかどうかわかりませんが、 'Input1.size()'が8の倍数でない場合、最後のスレッドは配列の最後から実行されます。 –

答えて

0

std::asyncへの呼び出しは、単一のスレッド上のすべてのasyncsを実行することができますlaunch::anyのデフォルトの起動ポリシーを、取得します。個別のスレッドを要求するには、std::asyncの呼び出しで最初の引数としてlaunch::asyncを渡します。

+0

ちょっと!私はこのlaunch :: asyncフラグをインクルードしようとしましたが、コードをdramaticalllyly遅く実行させます。これは、おそらくこのコードの大きな欠陥を指摘していますか? – tallonj

0

どのくらいのデータを投げるかによって異なります。

次の例では、単純なループで4096のエントリが高速になります。しかし、1000 * 4096のエントリでは、パラレルバージョンは高速です。

したがって、20-30%の改善の結果は、問題のハードウェアのその範囲の間にたどり着いている可能性があります。

私が使用したテストプログラムです。

最初の実行は単純なループであり、2番目は質問からのもので、3番目のものはstd::launch::asyncのものです。

Plain  From  With 
loop  question launch::async 
First  Second  Third 
166   1067  607  
166   614   434  
166   523   509  
265993  94633  66231  
182981  60594  69537  
237767  65731  57256 

ここではlive resultです。

#include <vector> 
#include <thread> 
#include <future> 
#include <complex> 
#include <string> 
#include <iostream> 
#include <chrono> 
#include <random> 
#include <ratio> 

float get_random() 
{ 
    static std::default_random_engine e; 
    static std::uniform_real_distribution<> dis(0,1); // rage 0 - 1 
    return static_cast<float>(dis(e)); 
} 

void do_tests(float val1, float val2, float val3, float val4, int multiplier) 
{ 
    { 
     std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2}); 
     std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4}); 
     std::complex<float> ResSum(0,0); 
     auto start{std::chrono::high_resolution_clock::now()}; 

     size_t size = Input1.size(); 
     for (int i=0; i<size; ++i) { 
      ResSum += Input1[i]*Input2[i]; 
     } 

     auto end{std::chrono::high_resolution_clock::now()}; 
     auto time_used{end-start}; 
     std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t"; 
    } 

    { 
     std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2}); 
     std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4}); 
     std::complex<float> ResSum(0,0); 
     auto start{std::chrono::high_resolution_clock::now()}; 

     size_t size = Input1.size()/8; 
     std::vector<std::future<std::complex<float>>> futures; 
     futures.reserve(8); 

     for (int i = 0; i<8; ++i) { 
      futures.push_back(
       std::async(
        [](std::complex<float>* pos,std::complex<float>*pos2,size_t siz) { 
       std::complex<float> resum(0,0); 
       for (int i = 0; i < siz; ++i) 
        resum += pos[i]*pos2[i]; 
       return resum; 
      } 
        ,&Input1[i*size],&Input2[i*size],size 
       ) 
       ); 
     } 

     for (int i = 0; i < futures.size(); ++i) 
      ResSum += futures.at(i).get(); 

     auto end{std::chrono::high_resolution_clock::now()}; 
     auto time_used{end-start}; 
     std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t"; 
    } 


    { 
     std::vector<std::complex<float>> Input1(4096*multiplier,std::complex<float>{val1,val2}); 
     std::vector<std::complex<float>> Input2(4096*multiplier,std::complex<float>{val3,val4}); 
     std::complex<float> ResSum(0,0); 
     auto start{std::chrono::high_resolution_clock::now()}; 

     size_t size = Input1.size()/8; 
     std::vector<std::future<std::complex<float>>> futures; 
     futures.reserve(8); 

     for (int i = 0; i<8; ++i) { 
      futures.push_back(
       std::async(std::launch::async, 
        [](std::complex<float>* pos,std::complex<float>*pos2,size_t siz) { 
       std::complex<float> resum(0,0); 
       for (int i = 0; i < siz; ++i) 
        resum += pos[i]*pos2[i]; 
       return resum; 
      } 
        ,&Input1[i*size],&Input2[i*size],size 
       ) 
       ); 
     } 

     for (int i = 0; i < futures.size(); ++i) 
      ResSum += futures.at(i).get(); 

     auto end{std::chrono::high_resolution_clock::now()}; 
     auto time_used{end-start}; 
     std::cout << std::chrono::duration_cast<std::chrono::microseconds>(time_used).count() << "\t\t"; 
    } 

    std::cout << '\n'; 

} 

int main() 
{ 
    float val1{get_random()}; 
    float val2{get_random()}; 
    float val3{get_random()}; 
    float val4{get_random()}; 

    std::cout << "First\t\tSecond\t\tThird\n"; 

    do_tests(val1, val2, val3, val4, 1); 
    do_tests(val1, val2, val3, val4, 1); 
    do_tests(val1, val2, val3, val4, 1); 
    do_tests(val1, val2, val3, val4, 1000); 
    do_tests(val1, val2, val3, val4, 1000); 
    do_tests(val1, val2, val3, val4, 1000); 


}