为啥这个自动向量器关心构造函数/析构函数?
Posted
技术标签:
【中文标题】为啥这个自动向量器关心构造函数/析构函数?【英文标题】:Why Does This Auto-Vectorizer Care About Constructors/Destructors?为什么这个自动向量器关心构造函数/析构函数? 【发布时间】:2015-05-15 22:49:16 【问题描述】:这是SSCCE:
class Vec final
public:
float data[4];
inline Vec(void)
inline ~Vec(void)
;
Vec operator*(float const& scalar, Vec const& vec)
Vec result;
#if 1
for (int k=0;k<4;++k) result.data[k]=scalar*vec.data[k];
#else
float const*__restrict src = vec.data;
float *__restrict dst = result.data;
for (int k=0;k<4;++k) dst[k]=scalar*src[k];
#endif
return result;
int main(int /*argc*/, char* /*argv*/[])
Vec vec;
Vec scaledf = 2.0f * vec;
return 0;
编译时,MSVC 2013 通知我 (/Qvec-report:2
)
main.cpp(11) : info C5002: loop not vectorized due to reason '1200'
这意味着“[l]oop 包含循环携带的数据依赖”。
我注意到注释Vec
的构造函数或析构函数(编辑:或默认它们,例如Vec()=default;
)会使其向量化成功。我的问题:为什么?
注意:切换#if
也会使其工作。 __restrict
很重要。
注意:将float const& scalar
更改为float const scalar
会导致矢量化报告1303
(矢量化不会成功),我怀疑是因为引用可以直接传递到SSE 寄存器,而传递值需要另一个复制。
【问题讨论】:
如果你默认 (Vec() = default;
) 构造函数和析构函数会发生什么?您现在定义它们的方式使它们变得不平凡,也许优化器出于某种原因不喜欢那样。
@Praetorian 默认其中至少一个会导致矢量化成功。
看起来(在我看来)目标类型的代码是否足够复杂,但它不够“智能”以确保您没有别名(即,它不能证明src
和dst
必然指向不同的对象)。
谨慎的后端人员会忽略 const 关键字。以“正常”形式编写它并将第一个参数作为const float scalar
传递,而不是通过引用传递它。所以它可以完全排除 scalar 是 float[] 中的元素的可能性。
如果将noexcept
限定符添加到ctor/dtor 会发生什么(就像= default
版本一样)
【参考方案1】:
为什么要声明一个空的非虚拟析构函数inline ~Vec(void)
和一个空的默认构造函数inline Vec(void)
?
因此编译器不会生成默认的复制构造函数。因此,没有它就无法编译代码return result;
,因为这需要将结果复制到一个临时返回的对象中(这可能不是您想要的)。
要么定义一个复制构造函数,要么根本不定义空的构造函数和析构函数。
【讨论】:
空的构造函数/析构函数是因为这个玩具示例的玩具性。此外,它必须生成一个复制构造函数,因为它编译得很好。无论如何,添加inline Vec(Vec const& other) = default;
似乎没有任何效果。以上是关于为啥这个自动向量器关心构造函数/析构函数?的主要内容,如果未能解决你的问题,请参考以下文章