在GCC 8环境下C++开发中能否使用SIMD三角函数优化旋转计算性能?
问题描述
我编写了一个使用SIMD实现四个3D点绕任意3D轴旋转的函数,功能可正常运行但性能表现极差,相比调用单次仅旋转一个点的函数执行四次仅快15%,远低于我预期的200%~300%性能提升。目前存在的一个问题是编译器无法识别SIMD三角函数,即便我能调用相关函数,也不确定所用CPU(Ryzen 3 1200和Ryzen 7 3700)是否支持该特性。
我希望使用SIMD计算椭圆轨道,该场景涉及大量三角函数运算。我在网上搜索多时未找到有效解决方案,相关代码如下:
vec12d MathFunction::Rotate_SIMD(const vec4d& angle, const vec12d& axis, const vec12d& point) { vec4d get_cosine = vec4d(cos(angle.v[0]), cos(angle.v[1]), cos(angle.v[2]), cos(angle.v[3])); vec4d get_sine = vec4d(sin(angle.v[0]), sin(angle.v[1]), sin(angle.v[2]), sin(angle.v[3])); __m256d cosTheta = _mm256_loadu_pd(get_cosine.v); __m256d sinTheta = _mm256_loadu_pd(get_sine.v); __m256d one = _mm256_set1_pd(1.0); __m256d minus_cosTheta = _mm256_sub_pd(one, cosTheta); __m256d Xaxis = _mm256_loadu_pd(axis.v); __m256d Yaxis = _mm256_loadu_pd(&axis.v[4]); __m256d Zaxis = _mm256_loadu_pd(&axis.v[8]); __m256d Xpoint = _mm256_loadu_pd(point.v); __m256d Ypoint = _mm256_loadu_pd(&point.v[4]); __m256d Zpoint = _mm256_loadu_pd(&point.v[8]); // Get new Xaxis points __m256d Xnew = _mm256_mul_pd(Xaxis, Xaxis); Xnew = _mm256_mul_pd(Xnew, minus_cosTheta); Xnew = _mm256_add_pd(Xnew, cosTheta); Xnew = _mm256_mul_pd(Xnew, Xpoint); __m256d temp = _mm256_mul_pd(minus_cosTheta, Xaxis); temp = _mm256_mul_pd(temp, Yaxis); __m256d temp2 = _mm256_mul_pd(Zaxis, sinTheta); temp = _mm256_sub_pd(temp, temp2); temp = _mm256_mul_pd(temp, Ypoint); Xnew = _mm256_add_pd(Xnew, temp); temp = _mm256_mul_pd(minus_cosTheta, Xaxis); temp = _mm256_mul_pd(temp, Zaxis); temp2 = _mm256_mul_pd(Yaxis, sinTheta); temp = _mm256_add_pd(temp, temp2); temp = _mm256_mul_pd(temp, Zpoint); Xnew = _mm256_add_pd(Xnew, temp); // Get new Yaxis points __m256d Ynew = _mm256_mul_pd(minus_cosTheta, Xaxis); Ynew = _mm256_mul_pd(Ynew, Yaxis); temp2 = _mm256_mul_pd(Zaxis, sinTheta); Ynew = _mm256_add_pd(Ynew, temp2); Ynew = _mm256_mul_pd(Ynew, Xpoint); temp = _mm256_mul_pd(Yaxis, Yaxis); temp = _mm256_mul_pd(temp, minus_cosTheta); temp = _mm256_add_pd(temp, cosTheta); temp = _mm256_mul_pd(temp, Ypoint); Ynew = _mm256_add_pd(Ynew, temp); temp = _mm256_mul_pd(minus_cosTheta, Yaxis); temp = _mm256_mul_pd(temp, Zaxis); temp2 = _mm256_mul_pd(Xaxis, sinTheta); temp = _mm256_sub_pd(temp, temp2); temp = _mm256_mul_pd(temp, Zpoint); Ynew = _mm256_add_pd(Ynew, temp); // Get new Zaxis points __m256d Znew = _mm256_mul_pd(minus_cosTheta, Xaxis); Znew = _mm256_mul_pd(Znew, Zaxis); temp2 = _mm256_mul_pd(Xaxis, sinTheta); Znew = _mm256_sub_pd(Ynew, temp2); Znew = _mm256_mul_pd(Ynew, Xpoint); temp = _mm256_mul_pd(minus_cosTheta, Yaxis); temp = _mm256_mul_pd(temp, Zaxis); temp2 = _mm256_mul_pd(Xaxis, sinTheta); temp = _mm256_add_pd(temp, temp2); temp = _mm256_mul_pd(temp, Ypoint); Znew = _mm256_add_pd(Znew, temp); temp = _mm256_mul_pd(minus_cosTheta, Zaxis); temp = _mm256_mul_pd(temp, Zaxis); temp = _mm256_add_pd(temp, cosTheta); temp = _mm256_mul_pd(temp, Zpoint); Znew = _mm256_add_pd(Znew, temp); vec12d Npoint __attribute__ ((aligned(32))); // Four points rotated goes here. _mm256_store_pd(Npoint.v, Xnew); _mm256_store_pd(&Npoint.v[4], Ynew); _mm256_store_pd(&Npoint.v[8], Znew); return Npoint; }
解决方案
核心性能瓶颈说明
当前代码性能拉胯的核心原因是8次标量sin/cos调用占了绝大部分耗时,SIMD旋转部分的开销被三角函数完全覆盖,自然看不到预期的性能提升。你使用的两款CPU(Ryzen 3 1200为Zen1架构、Ryzen 7 3700为Zen2架构)均支持AVX2指令集,虽无原生硬件三角函数指令,但可通过向量数学库实现批量三角函数计算,性能远超标量逐次调用。
现有代码隐藏问题
- Z轴计算逻辑存在明显的复制粘贴错误:
Znew = _mm256_sub_pd(Ynew, temp2);后续还用这个错误值乘Xpoint,只是当前测试用例未触发异常,建议优先修正该逻辑。 - 所有输入都使用了非对齐加载
_mm256_loadu_pd,如果能保证输入的vec4d、vec12d都做32字节对齐,换成对齐加载指令可获得小幅性能提升。
可落地优化方案
- 替换三角函数实现:如果用GCC编译,添加编译参数
-mavx2 -ffast-math -lmvec,glibc的libmvec库会自动将逐次调用的标量sin/cos向量化,一次计算4个double的三角函数值,仅这一步就能把三角函数部分的性能提升2~3倍。 - 调整运算逻辑:如果多组点共用同一个旋转轴和角度,提前把罗德里格斯旋转矩阵的9个系数预计算完成,再批量处理点,避免每次处理4个点都重复计算相同的矩阵系数。
- 精度允许的情况下改用单精度浮点数:AVX2一次可以处理8个float,吞吐量直接翻倍,椭圆轨道计算场景下通常单精度就足够满足精度要求,整体性能会有非常明显的提升。
内容的提问来源于stack exchange,提问作者Joel P
相关产品推荐
相关产品推荐

