SIMD - Single Instruction Multiple Data
现代微处理器中使用一条指令进行多个数据运算的技术
对于矢量运算和矩阵运算可以迅速地执行
被广泛运用于游戏引擎的数学库中
对于矢量和矩阵的运算(比如使用矩阵对矢量进行变换)
将四个浮点数打包进入128bit的寄存器
于是对于加法和乘法,可以使用一条指令对四个浮点数同时进行运算
四个浮点数可以个别读写
但是速度较慢(因为需要在FPU和SSE的寄存器之间传输数据)
因此应尽量避免普通float运算和SIMD运算的混合使用。
而在使用SIMD做运算时就应尽量将数据保存在SSE寄存器中做运算
即使是标量与向量的运算,也将标量复制为4份同时进行运算
在 Visual Studio 中 (为了区分两个下划线,博主手动在两条下划线之间添加了一个空格方便您查看)
使用内建的 _ _m128 类型的数据即可使用这个优化的方法
以 _ _m128 类型作为自动变量或者参数时编译器会将他直接置于SSE寄存器中
注意,_ _m128 类型在内存中时
要将其地址以 16 字节对齐,即是变量地址最低有效半字节需为 0x0
使用自动变量时编译器会自动进行管理
但是动态分配时则需要程序猿手动保证哦~
操作_ _m128 类型时需要包含 xmmintrin.h 头文件中的内部函数
下面例子将两个 1X4 的数组内容相加并输出
_ _declspec(align(16)) 使变量使用16字节进行内存对齐
#include
#include
_ _declspec(align(16)) float A[] = {1.0f, 2.0f, 3.0f, 4.0f};
_ _declspec(align(16)) float B[] = {4.0f, 3.0f, 2.0f, 1.0f};
_ _declspec(align(16)) float C[4] = {0.0f};
int main(void)
{
_ _m128 a = _mm_load_ps(A);
_ _m128 b = _mm_load_ps(B);
_ _m128 c = _mm_add_ps(a, b);
_mm_store_ps(C, c);
printf("%.2f, %.2f, %.2f, %.2f\n", C[0], C[1], C[2], C[3]);
return 0;
}
在3D游戏中
常在3D向量末尾添加一个元素(通常为1.0)作为齐次坐标
于是3D向量就成了一个 1X4 的矩阵
而仿射矩阵为 4X4 矩阵
使用 1X4 矩阵与 4X4 矩阵进行乘法运算
其结果即是对坐标进行矩阵变换后的坐标
使用 SIMD 对运算进行优化
技巧在于将齐次坐标和矩阵的行或列分别视为一个 _ _m128 中的四个元素
将齐次坐标中的 xyzw 每一个分别填充一个 _ _m128
将矩阵中每一行的四个元素填充一个 _ _m128
// 使用矩阵对矢量进行变换 - SIMD 方法
inline __m128 TransformVectorByMatrix_Quick(
const __m128 &mvec,
const __m128 &mmline1,
const __m128 &mmline2,
const __m128 &mmline3,
const __m128 &mmline4)
{
__m128 mrowx = _mm_mul_ps(mmline1, _mm_shuffle_ps(mvec, mvec, _MM_SHUFFLE(0, 0, 0, 0)));
__m128 mrowy = _mm_mul_ps(mmline2, _mm_shuffle_ps(mvec, mvec, _MM_SHUFFLE(1, 1, 1, 1)));
__m128 mrowz = _mm_mul_ps(mmline3, _mm_shuffle_ps(mvec, mvec, _MM_SHUFFLE(2, 2, 2, 2)));
__m128 mroww = _mm_mul_ps(mmline4, _mm_shuffle_ps(mvec, mvec, _MM_SHUFFLE(3, 3, 3, 3)));
__m128 result0 = _mm_add_ps(mrowx, mrowy);
__m128 result1 = _mm_add_ps(mrowz, mroww);
return _mm_add_ps(result0, result1);
}