0

David Vandevoorde 著「C++ Template」にあるものと同様の式テンプレートのプロファイルを作成しようとしています。以下は私の理論的分析です。テストが予期しない結果を示しているため、おそらく間違っています。テストが次の内容であるとします。

R = A + B + C;

ここで、A、B、C、R はヒープに割り当てられた配列です。配列のサイズは 2 です。したがって、次のように実行されます。

R[0] = A[0] + B[0] + C[0]; // 3 loads + 2 additions + 1 store
R[1] = A[1] + B[1] + C[1];

約 12 命令 (それぞれ 6 命令) を使用します。

ここで、式テンプレートが有効になっている場合 (一番下に表示)、コンパイラ時に型推定が行われた後、上記と同じ評価が実行される前に、実行時に以下が処理されます。

A + B --> expression 1 // copy references to A & B 
expression 1 + C --> expression 2 // copy the copies of references to A & B
                                  // + copy reference to C

したがって、評価前に合計 2+3=5 命令があり、これは合計命令の約 5/(5+12)=30% です。そのため、特にベクトル サイズが小さい場合に、このオーバーヘッドを確認できるはずです。

しかし、結果は、2 つのコストがほぼ同じであることを示しています。テストを 1E+09 回繰り返します。もちろん、2 つのアセンブリ コードは同じです。しかし、この「施工」の部分に手間がかかる部品や説明書が見つかりませんでした。

movsdq  (%r9,%rax,8), %xmm0
addsdq  (%r8,%rax,8), %xmm0
addsdq  (%rdi,%rax,8), %xmm0
movsdq  %xmm0, (%rcx,%rax,8)

私はCSのバックグラウンドが良くないので、この質問はとてもばかげているかもしれません. しかし、私はこれについて何日も頭を悩ませてきました。ですから、どんな助けも大歓迎です!

--- 私の表現テンプレート ---

template< typename Left, typename Right >
class V_p_W // stands for V+W
{
public:
   typedef typename array_type::value_type             value_type;
   typedef double                                      S_type;
   typedef typename traits< Left >::type               V_type;
   typedef typename traits< Right >::type              W_type;

   V_p_W ( const Left& _v, const Right& _w ) : V(_v), W(_w)
   {}

   inline value_type operator [] ( std::size_t i )        { return V[i] + W[i]; }
   inline value_type operator [] ( std::size_t i ) const  { return V[i] + W[i]; }
   inline std::size_t size () const                       { return V.size();  }

private:
   V_type V;
   W_type W;
};

where traits は、オブジェクトの参照の値を取得するかどうかを決定するだけです。たとえば、値は整数に対してコピーされますが、参照は配列に対して取得されます。

4

1 に答える 1

0

私の自家醸造テスト。理想的には、式テンプレートは、単純なケースで一時的なベクトルに必要な余分な割り当てを節約します。

expr.cpp:

#include <vector>
#include <stdlib.h>
#include <iostream>
#include <ctime>

using namespace std;

typedef vector<int> Valarray;

template<typename L, typename R>
struct BinOpPlus {
  const L& left;
  const R& right;

  BinOpPlus(const L& l, const R& r)
    : left(l), right(r)
  {}

  int operator[](int i) const { return left[i] + right[i]; }
};

template<typename L, typename R>
BinOpPlus<L, R> operator+(const L& left, const R& right){
  return BinOpPlus<L, R>(left, right);
}

int main() {
  int size = 10000000;
  Valarray v[3];
  for(int n=0; n<3; ++n){
    for(int i=0; i<size; ++i){
      int val = rand() % 100;
      v[n].push_back(val);
    }
  }

  std::clock_t start = std::clock();
  auto tmp = v[0] + v[1];
  auto out = tmp + v[2];

  int sum = 0;
  for(int i=0; i<size; ++i)
    sum += out[i];

  std::clock_t stop = std::clock();
  cout << "Sum: " << sum << endl;
  cout << "Time: " << (stop-start) << endl;
  return 0;
}

vala.cpp:

#include <vector>
#include <stdlib.h>
#include <iostream>
#include <ctime>

using namespace std;

class Valarray : public vector<int> {
  public:
    Valarray operator+(const Valarray& r) const {
      Valarray out;
      out.reserve(r.size());
      for(size_t i=0; i<r.size(); ++i)
        out.push_back((*this)[i] + r[i]);
      return out;
    }

    Valarray operator+(Valarray&& r) const {
      for(size_t i=0; i<r.size(); ++i)
        r[i] = (*this)[i] + r[i];
      return r;
    }
};

int main() {
  int size = 10000000;
  Valarray v[3];
  for(int n=0; n<3; ++n){
    for(int i=0; i<size; ++i){
      int val = rand() % 100;
      v[n].push_back(val);
    }
  }

  std::clock_t start = std::clock();
  Valarray out = v[0] + v[1] + v[2];

  int sum = 0;
  for(int i=0; i<size; ++i)
    sum += out[i];

  std::clock_t stop = std::clock();
  cout << "Sum: " << sum << endl;
  cout << "Time: " << (stop-start) << endl;
  return 0;
}

コマンドライン:

g++ -Wfatal-errors -std=c++11 -Wall -Werror vala.cpp -o vala
g++ -Wfatal-errors -std=c++11 -Wall -Werror expr.cpp -o expr
~/example$ ./vala
Sum: 1485274472
Time: 680000
~/example$ ./expr
Sum: 1485274472
Time: 130000

最適化あり:

g++ -Wfatal-errors -std=c++11 -Wall -Werror vala.cpp -o vala -O3
g++ -Wfatal-errors -std=c++11 -Wall -Werror expr.cpp -o expr -O3
na:~/example$ ./vala
Sum: 1485274472
Time: 290000
na:~/example$ ./expr
Sum: 1485274472
Time: 10000

エクスプレッション テンプレートを使用すると、余分なベクトルの割り当てが回避されるため、大幅に改善されます。

于 2015-01-29T19:46:44.143 に答える