boost::numeric::ublas::vectorの速度比較

boost/numeric/ublas/vectorの生成と代入に関する速度をまとめた。

素数を5千万にして生成と代入の速度を計測。

compiler及び環境

clock関数で時間を計測。時間の分解能はμ秒。

時間はコンパイル時にO2オプションをつけた時と付けない時の時間を記載。

生成と初期化の速度

まとめ

  • ublas::vectorstd::vectorの生成に関する時間の差はなし。

  • vectorのfor文での全要素の走査は生成の6倍程度の時間がかかる。但し、O2オプションをつければ生成と同程度の時間ですむ。

    • 生成後の代入操作はなるべく避ける。

std::vectorの生成の時間

std::vector<double> vec1(num, 2.0);

上記コードのstd::vectorの生成の時間は

  • O2なし:0.379705[sec]
  • O2あり:0.244777[sec]

boost::numeric::ublas::vectorの生成の時間

boost::numeric::ublas::vector<double> vec3(num, 1.2);

上記コードのboost::numeric::ublas::vectorの生成の時間

  • O2なし:0.408235[sec]
  • O2あり:0.249085[sec]

boost::numeric::ublas::vectorの走査時間

    for (std::size_t index = 0; index < num; ++index) {
        vec3[index] = index / 2.0;
    }

上記コードのboost::numeric::ublas::vectorの走査時間

  • O2なし:1.27493[sec]
  • O2あり:O2:0.279947[sec]

代入に関する時間

前提として下記コード内のvec2,vec3は以下のように要素数5千万のublas::vectorとして宣言されているとする。

const std::size_t num = 50000000;
boost::numeric::ublas::vector<double> vec2(num);
boost::numeric::ublas::vector<double> vec3(num);

まとめ

  • vec2=vec3の代入が最も高速。

  • for文での要素毎の代入はvec2=vec3の4倍程度かかる。

  • 関数への参照渡しは要素数にかかわらずほぼ時間0と考えて良い。

  • 関数への実体渡しは、vectorの生成にかかる時間と同程度かやや遅い。

  • 関数への実体戻しでの代入は、for文での代入と同じくらい遅い。

  • 参照渡しで関数内でvec2=vec3をするのと、関数外でvec2=vec3は速度はほぼ同じ。

vec2=vec3の速度

    vec3[1] = vec2[10];
    vec2 = vec3;
    vec2[1] = vec3[2];
  • O2なし:0.079151
  • O2あり:0.075633

for文による要素毎の代入の速度速度

    for (std::size_t index = 0; index < num; ++index) {
        vec2[index] = vec3[index];
    }
  • O2なし:1.83324
  • O2あり:0.3431

生成のときのfor文の倍の時間かかっているのは、operator[]が二個あるため。

関数の参照渡しと戻り値を実体戻し

boost::numeric::ublas::vector<double> hoge(
    boost::numeric::ublas::vector<double>& vec)
{
    return vec;
}
    vec3 = hoge(vec2);
  • O2なし:0.408114
  • O2あり:0.317184

for文と同じくらい遅い

関数の参照渡しと参照戻し

boost::numeric::ublas::vector<double>& hoge2(
    boost::numeric::ublas::vector<double>& vec)
{
    return vec;
}
    vec3 = hoge2(vec2);
   
  • O2なし:0.073233
  • O2あり:0.075371

おそらく、vec2=vec3と内部的には同じ動作。

参照渡しにかかる時間

void hoge3(
    boost::numeric::ublas::vector<double>& vec)
{
}
    hoge3(vec3);
  • O2なし:1e-06
  • O2あり:0

参照渡しにかかる時間はほぼゼロ。O2で0[sec]になっているのはおそらく関数内にコードがないので、関数呼び出しを省略されたため。

関数の実体渡しにかかる時間

void hoge4(
    boost::numeric::ublas::vector<double> vec)
{
}
    hoge4(vec3);
  • O2なし:0.264846
  • O2あり:0.249281

実体渡しは生成とほぼ同じ時間がかかる。

参照渡しで関数内でvec2=vec3

void hoge5(
    boost::numeric::ublas::vector<double>& vec,
    boost::numeric::ublas::vector<double>& vec2)
{
    vec2 = vec;
}
    hoge5(vec2, vec3);
  • O2なし:0.075753
  • O2あり:0.074778

コード全体

時間は時間計測開始部分にコメントで以下のように付与している。

//O2オプションなしの時間[sec]
//O2オプションありの時間[sec]
start = clock()
//処理など
#include <boost/numeric/ublas/vector.hpp>
#include <vector>

#include <time.h>

boost::numeric::ublas::vector<double> hoge(
    boost::numeric::ublas::vector<double>& vec)
{
    return vec;
}

boost::numeric::ublas::vector<double>& hoge2(
    boost::numeric::ublas::vector<double>& vec)
{
    return vec;
}

void hoge3(
    boost::numeric::ublas::vector<double>& vec)
{
}

void hoge4(
    boost::numeric::ublas::vector<double> vec)
{
}

void hoge5(
    boost::numeric::ublas::vector<double>& vec,
    boost::numeric::ublas::vector<double>& vec2)
{
    vec2 = vec;
}

int main(int argc, char const* argv[])
{
    namespace ublas = boost::numeric::ublas;
    
    const std::size_t num = 50000000;

    //0.379705
    //O2:0.244777
    clock_t start = clock();
    std::vector<double> vec1(num, 2.0);
    clock_t end = clock();
    double duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration1:" << duraiton << std::endl;

    boost::numeric::ublas::vector<double> vec2(num, 1.0);
    for (std::size_t index = 0; index < num; ++index) {
        vec2[index] = index;
    }

    //0.408235
    //O2:0.249085
    start = clock();
    boost::numeric::ublas::vector<double> vec3(num, 1.2);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration2:" << duraiton << std::endl;
    //1.27493
    //O2:0.279947
    start = clock();
    for (std::size_t index = 0; index < num; ++index) {
        vec3[index] = index / 2.0;
    }
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration3:" << duraiton << std::endl;

    //0.118211
    //O2:0.103219
    start = clock();
    vec3[1] = vec2[10];
    vec2 = vec3;
    vec2[1] = vec3[2];
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration4:" << duraiton << std::endl;

    //2.2732
    //O2:0.446582
    start = clock();
    for (std::size_t index = 0; index < num; ++index) {
        vec2[index] = vec3[index];
    }
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration5:" << duraiton << std::endl;

    //0.422054
    //O2:0.421855
    start = clock();
    vec3 = hoge(vec2);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration6:" << duraiton << std::endl;

    //0.119685
    //O2:0.115396
    start = clock();
    vec3 = hoge2(vec2);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration7:" << duraiton << std::endl;
    
    //1e-06
    //O2:0
    start = clock();
    hoge3(vec3);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration8:" << duraiton << std::endl;

    //0.346663
    //O2:0.259321
    start = clock();
    hoge4(vec3);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration9:" << duraiton << std::endl;

    //0.074641
    //O2:0.075302
    start = clock();
    hoge5(vec2, vec3);
    end = clock();
    duraiton = static_cast<double>((end - start)) / CLOCKS_PER_SEC;
    std::cout << "duration10:" << duraiton << std::endl;

    
    return 0;
}