我试图分析表达模板,类似于书中的表达模板" C ++模板"作者:David Vandevoorde。以下是我的理论分析,这可能是错误的,因为测试显示出意想不到的结果。 假设测试是关于:
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%。所以我应该能够看到这种开销,特别是当矢量大小很小时。
但结果显示两者的成本几乎相同。我迭代测试1E + 09次。当然,两者的汇编代码是相同的。但我无法找到这个"建筑的部分"任何时间或指示费用的部分。
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;
};
其中traits只做决定是否应该采用对象引用的值。例如,为整数复制该值,但是对数组进行引用。
答案 0 :(得分: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
使用表达式模板进行大量改进,因为它避免了额外的矢量分配。