如何让MSVC识别结构体内部指针不存在别名
MSVC中__restrict关键字对结构体包裹指针的优化问题
MSVC的__restrict关键字直接作用于裸指针时可以正常触发优化,但当指针被结构体包裹后,该关键字的优化效果就消失了。
我编写了如下测试代码:
#include <memory> #include <intrin.h> float dot(__m128 a,__m128 b) { __m128 tmp = _mm_mul_ps(a,b); tmp = _mm_hadd_ps(tmp,tmp); return _mm_cvtss_f32(_mm_hadd_ps(tmp,tmp)); } template<typename T,int Stride=1> struct v_span { T *data; operator __m128() const { if constexpr(Stride == 1) { return _mm_load_ps(data); } else { return _mm_set_ps(data[0],data[Stride],data[Stride*2],data[Stride*3]); } } T &operator[](int i) const { return data[i*Stride]; } }; template<typename T> struct m_span { T *data; auto row(int i) const { return v_span<T>{data + i*4}; } auto col(int i) const { return v_span<T,4>{data + i}; } }; void multiply1(m_span<float> out, m_span<const float> a, m_span<const float> b) { for(int c=0; c<4; ++c) { for(int r=0; r<4; ++r) { out.row(r)[c] = dot(a.row(r),b.col(c)); } } } void multiply2(float* __restrict out, m_span<const float> a, m_span<const float> b) { for(int c=0; c<4; ++c) { for(int r=0; r<4; ++r) { out[r*4 + c] = dot(a.row(r),b.col(c)); } } } void multiply3(float* __restrict _out, m_span<const float> a, m_span<const float> b) { m_span<float> out{_out}; for(int c=0; c<4; ++c) { for(int r=0; r<4; ++r) { out.row(r)[c] = dot(a.row(r),b.col(c)); } } }
用Godbolt以/O2 /std:c++20参数编译后观察汇编结果:
- multiply2的优化效果良好,
b.col(c)在外层循环中仅从内存加载一次; - multiply3生成的汇编与multiply1完全一致,每次循环都会重新加载
b.col(c); - 对比之下,Clang对multiply2和multiply3生成的汇编完全相同。
我尝试过以下方法,但都没有效果:
- 在v_span和m_span内部的指针成员上添加
__restrict关键字; - 创建包裹float的结构体并添加转换运算符,让out指向不同类型的指针,但发现MSVC默认允许不同类型间的别名,这与Clang和GCC的行为不同。
我清楚可以通过将b.col(c)存储到变量中来规避这个问题,也知道有更高效的计算方式,但这只是简化后的测试示例。实际项目中的函数是兼容固定大小和可变大小向量、矩阵的模板,存在更复杂的计算需求。虽然MSVC的优化能力不如Clang和GCC,并非我的首选编译器,但我希望项目能让任何人都能轻松下载并编译。
内容的提问来源于stack exchange,提问作者Rouslan
相关产品推荐
相关产品推荐

