2017-08-30 2 views
0

別のマトリックスのサブマートにマトリックスの値を割り当てたい場合は、A.submat(ni1, ni2, nk1, nk2) = B;非常に遅いです。なぜそれが遅いのだろうと思っていますし、それを改善する方法がいくつかありますか?ここでArmadillo C++:サブマートへのエフェクト割り当ての方法

は私のテストコードである(機能「XForwarDifferenceは」私のプロジェクトでは、何百万時間と呼ばれる必要があるので、私はより良いそれをプロファイルする必要があります)

#include <armadillo> 
#include <chrono> 
#include <iostream> 
using ms = std::chrono::milliseconds; 
using ns = std::chrono::nanoseconds; 
using get_time = std::chrono::steady_clock; 

namespace { 
    const arma::ivec::fixed<5> iforward = {-1, 0, 1, 2, 3}; 
    const double MCA_1 = -0.30874; 
    const double MCA0 = -0.6326; 
    const double MCA1 = 1.2330; 
    const double MCA2 = -0.3334; 
    const double MCA3 = 0.04168; 
} 

arma::mat XForwardDifference(arma::mat& mat, 
          const int& ni1, 
          const int& ni2, 
          const int& nk1, 
          const int& nk2, 
          const double& dx) 
{ 
    arma::mat ret(size(mat)); 
    double sign_dx = 1./dx; 
    double mca_1= sign_dx * MCA_1; 
    double mca0 = sign_dx * MCA0; 
    double mca1 = sign_dx * MCA1; 
    double mca2 = sign_dx * MCA2; 
    double mca3 = sign_dx * MCA3; 
    auto t1 = get_time::now(); 
    auto m1 = mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2); 
    auto t2 = get_time::now(); 
    auto m2 = mca_1 * m1; 
    auto t3 = get_time::now(); 
    auto m3 = m2 + m2; 
    auto t4 = get_time::now(); 
    mat.submat(ni1, nk1, ni2, nk2) = m3; 
    auto t5 = get_time::now(); 
    std::cout << std::chrono::duration_cast<ns>(t2-t1).count() << std::endl; 
    std::cout << std::chrono::duration_cast<ns>(t3-t2).count() << std::endl; 
    std::cout << std::chrono::duration_cast<ns>(t4-t3).count() << std::endl; 
    std::cout << std::chrono::duration_cast<ns>(t5-t4).count() << std::endl; 


    // ret.submat(ni1, nk1, ni2, nk2) = 
    // mca_1* mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2) + 
    // mca0 * mat.submat(ni1+iforward(1), nk1, ni2+iforward(1), nk2) + 
    // mca1 * mat.submat(ni1+iforward(2), nk1, ni2+iforward(2), nk2) + 
    // mca2 * mat.submat(ni1+iforward(3), nk1, ni2+iforward(3), nk2) + 
    // mca3 * mat.submat(ni1+iforward(4), nk1, ni2+iforward(4), nk2); 
    return ret; 
} 


int main(int argc, char *argv[]) 
{ 
    const int len = 3; 
    int ni1, ni2, nk1, nk2, ni, nk; 
    ni = 200; 
    nk = 200; 
    ni1 = len; 
    ni2 = ni1 + ni - 1; 
    nk1 = len; 
    nk2 = nk1 + nk - 1; 
    const double dx = 1.; 
    auto start_time = get_time::now(); 
    arma::mat mat(ni + 2*len, nk + 2*len); 
    mat = XForwardDifference(mat, ni1, ni2, nk1, nk2, dx); 
    auto end_time = get_time::now(); 
    auto diff = end_time - start_time; 
    std::cout << "Elapsed time is : " 
      << std::chrono::duration_cast<ns>(diff).count() 
      << " ns " 
      << std::endl; 
    return 0; 
} 

出力は次のようになります。

180 
116 
110 
851123 
Elapsed time is : 961975 ns 

mat.submat(ni1, nk1, ni2, nk2) = m3;はほとんどの経過時間をカバーしています。

hbrerkereは、理由を与える:

結果は、マトリックスまたは部分行列に割り当てられるまでアルマジロは、すべての操作をキューに入れます。このため、サブマートへの割り当てに時間がかかるようです。これは実際に乗算と加算を割当て時に行います。また、Armadilloの行列と式でautoキーワードを使用しないでください。問題が発生する可能性があります。 - 彼が言ったように、私はコードを変更する場合hbrerkere

auto t1 = get_time::now(); 
    arma::mat m1 = mat.submat(ni1+iforward(0), nk1, ni2+iforward(0), nk2); 
    auto t2 = get_time::now(); 
    arma::mat m2 = mca_1 * m1; 
    auto t3 = get_time::now(); 
    arma::mat m3 = m2 + m2; 
    auto t4 = get_time::now(); 
    ret = m3; 
    auto t5 = get_time::now(); 

、その後、出力は現在以下の通りです:

391880 
356480 
373072 
113051 
Elapsed time is : 1352013 ns 

私もautoはアルマジロに問題をもたらすという事態が発生しました。

auto m1 = mat.submat(ni1, nk1, ni2, nk2) * 2; 
cout << size(m1) << endl; 

これは正しくない非常に大きなサイズを印刷します。

+1

アルマジロは結果がマトリックスまたはサブマトリックスに割り当てられるまですべての操作をキューに入れます。このため、サブマートへの割り当てに時間がかかるようです。これは実際に乗算と加算を割当て時に行います。 また、Armadilloの行列と式で_auto_キーワードを使用しないでください。問題が発生する可能性があります。 – hbrerkere

+0

@hbrerkereあなたの答えは私にとって非常に役に立ちます。 'auto'の代わりに' arma :: mat'を使用すると、あなたが言ったようにコスト時間が変わります。 – Aristotle0

答えて

0

あなたのコードでは遅くする必要がある特定の点はないようです。あなたがしていることは上手く見えます。私が示唆する唯一のことは、submatコールの数を最小限に抑えることです。一時的なマトリックスを作成する可能性があるため、比較的高価であると考えられます。 submatを使わずにやりたい数学をすることを検討してください。インデックスを計算し、自分で割り当てます。

私は2つの他の提案持っている、ということを考える:あなたはそこに、改善するために、あなたのコードか何かと間違って何かすること、それを書くことに別の方法を検討する(可能な場合)とfunction profilerを使用する可能性があると主張している場合

  1. をValgrindのように、どの機能が最も費用がかかり、最適化できるかどうかを調べることができます。

  2. これをあきらめたら、C++でマルチスレッドを行う方法を学ぶことを検討してください。今日は、とても簡単です。どちらかを使用してくださいOpenMP、これは超簡単です...しかし、あなたは間違いを簡単に(high contentionのように)それを行う必要があります。あるいは単にスレッド内で関数を実行する比較的新しいC++構造体であるstd::threadを使用してください。あなたのケースでは、あなたのアプリケーションは反復的なものなので、それらのどれかを使うことができます。 my simple thread pool実装を使用すると、新しいインスタンスの呼び出しが完了するとすぐにそのインスタンスの呼び出しをスケジュールします。

幸運。

関連する問題