我想了解在C ++表达式模板的概念,因为这样我鹅卵石的示例代码等综合在一起,产生一个简单的载体和相关的表达式模板架构只支持二元运算符(+, - ,*)。
一切编译,但是我注意到标准手写循环与表达模板变种之间的性能差异是相当大的。 ET是因为手写的近两倍慢。 我期望的差异,但并不多。
一个完整的代码可以在这里找到:
https://gist.github.com/BernieWt/769a4a3ceb90bb0cae9e
(道歉乱码。)
。
总之,我基本上是比较以下两个循环:
AND:
for (std::size_t i = 0 ; i < rounds; ++i)
{
v4 = ((v0 - v1) + (v2 * v3)) + v4;
total += v4[0];
}
HW:
for (std::size_t i = 0 ; i < rounds; ++i)
{
for (std::size_t x = 0; x < N; ++x)
{
v4[x] = (v0[x] - v1[x]) + (v2[x] * v3[x]) + v4[x];
}
total += v4[0];
}
当我拆开输出,以下是生产的,所不同的是清楚地表明,ET变体的返回期间发生的额外memcpy和几个64位的负载:
Standard Loop | Expression Template
----------------------------------------+--------------------------------
L26: | L12:
xor edx, edx | xor edx, edx
jmp .L27 | jmp .L13
L28: | L14:
movsd xmm3, QWORD PTR [rsp+2064+rdx*8] | movsd xmm3, QWORD PTR [rsp+2064+rdx*8]
L27: | L13:
movsd xmm2, QWORD PTR [rsp+1040+rdx*8] | movsd xmm1, QWORD PTR [rsp+1552+rdx*8]
movsd xmm1, QWORD PTR [rsp+16+rdx*8] | movsd xmm2, QWORD PTR [rsp+16+rdx*8]
mulsd xmm2, QWORD PTR [rsp+1552+rdx*8] | mulsd xmm1, QWORD PTR [rsp+1040+rdx*8]
subsd xmm1, QWORD PTR [rsp+528+rdx*8] | subsd xmm2, QWORD PTR [rsp+528+rdx*8]
addsd xmm1, xmm2 | addsd xmm1, xmm2
addsd xmm1, xmm3 | addsd xmm1, xmm3
movsd QWORD PTR [rsp+2064+rdx*8], xmm1 | movsd QWORD PTR [rsp+2576+rdx*8], xmm1
add rdx, 1 | add rdx, 1
cmp rdx, 64 | cmp rdx, 64
jne .L28 | jne .L14
| mov dx, 512
| movsd QWORD PTR [rsp+8], xmm0
| lea rsi, [rsp+2576]
| lea rdi, [rsp+2064]
| call memcpy
movsd xmm3, QWORD PTR [rsp+2064] | movsd xmm0, QWORD PTR [rsp+8]
sub rcx, 1 | sub rbx, 1
| movsd xmm3, QWORD PTR [rsp+2064]
addsd xmm0, xmm3 | addsd xmm0, xmm3
jne .L26 | jne .L12
我的问题是:在这一点上我卡在如何去消除副本,我基本上是想不复制更新到位V4。 如何去这样做的任何想法?
注1:我试过GCC 4.7 / 9,铛3.3,VS2010 / 2013 -我得到大致相同的性能配置中提到的所有的编译器。
注2:我也尝试着声明bin_exp的VEC,然后添加以下赋值运算符和删除bin_exp转换操作符, 但无济于事 :
template<typename LHS, typename RHS, typename Op>
inline vec<N>& operator=(const bin_exp<LHS,RHS,Op,N>& o)
{
for (std::size_t i = 0; i < N; ++i) { d[i] = o[i]; }
return *this;
}
更新注2:提出的解决方案实际上是正确的。 和不会导致编译器产生几乎相同的手写循环代码。
。
在另一方面,如果我重写用例为ET变种如下:
auto expr = ((v0 - v1) + (v2 * v3)) + v4;
//auto& expr = ((v0 - v1) + (v2 * v3)) + v4; same problem
//auto&& expr = ((v0 - v1) + (v2 * v3)) + v4; same problem
for (std::size_t i = 0 ; i < rounds; ++i)
{
v4 = expr
total += v4[0];
}
发生崩溃的原因是该ET的实例化过程中产生的临时对象(右值)之前将分配破坏。 我在想,如果有使用C ++ 11导致编译器错误的任何方式。