基于 System.Runtime.Intrinsics.X86 的指令级融合内核。
SIMDIntrinsics
00 Remarks
这里的函数都只负责“Double 与 Single 的 256 位 融合乘加(FMA)”这一类确实存在指令级收益的场景;普通的逐元素加减乘除 请使用 SimdEngine —— 因为 Vector256(Of Double) 的宽度 与 Vector(Of Double).Count 完全一致,再单独写一条 256 位路径只会 增加代码量而不会提升吞吐。
所有的 FMA 内核都会在运行期同时检查 SimdCapabilities.IsFma 与 SIMDEnvironment.IsEnabled:前者保证处理器确实有 FMA 指令, 后者保证 SIMDConfiguration.disable 这个全局逃生开关依然有效。 任一条件不满足时会退回到等价的 SimdEngine / SimdReduce 实现,因此这些函数在任何平台与任何配置下都是安全的。
与旧实现的关键差别:这里使用批量装载/存储 (New Vector256(Of T)(array, offset) + Vector256(Of T).CopyTo) 取代旧版的 Vector256.Create(...) 逐元素构造 + GetElement 逐元素回写(后者会因为多次插入/提取通道而失去向量化的全部收益)。
01 Syntax
02 Methods
| Name | Overloads | Summary |
|---|---|---|
| HorizontalSum4 | 1 | Double 的 4 通道水平求和。 |
| Load4 | 1 | 从 Double 数组的 offset 处批量装载一个 256 位向量。 |
| Load8 | 1 | 从 Single 数组的 offset 处批量装载一个 256 位向量。 |
| Store4 | 1 | 把一个 256 位向量批量写回 Double 数组。 |
| VectorAddAvx | 2 | 逐元素相加(Double)。 |
| VectorAddAvx2 | 2 | 逐元素相加(Double)。 |
| DotFma | 2 | 点积(Double),使用 FMA 融合乘加。 |
| SumSquaresFma | 1 | 平方和:SUM(v(i) ^ 2),使用 FMA 融合乘加。 |
| Axpy | 1 | AXPY:out(i) = alpha * x(i) + y(i),使用 FMA 融合乘加。 |
| AxpyInPlace | 1 | 就地 AXPY:y(i) += alpha * x(i),使用 FMA 融合乘加。 |
| MultiplyAdd | 1 | 融合乘加:out(i) = v1(i) * v2(i) + acc(i) |
03 Members
Vector256(Of Double))Double 的 4 通道水平求和。
Double(), Int32)从 Double 数组的 offset 处批量装载一个 256 位向量。
Single(), Int32)从 Single 数组的 offset 处批量装载一个 256 位向量。
Vector256(Of Double), Double(), Int32)把一个 256 位向量批量写回 Double 数组。
Double(), Double())逐元素相加(Double)。
保留这个名称仅为兼容旧代码;内部走 SimdEngine.Add() 这条跨平台的主干路径。
Single(), Single())逐元素相加(Single)。
Double(), Double())逐元素相加(Double)。
保留这个名称仅为兼容旧代码;对 Double 而言 AVX2 与 AVX 的浮点加法是同一条指令,因此与 SIMDIntrinsics.VectorAddAvx() 完全等价。
Single(), Single())逐元素相加(Single)。
Double(), Double())点积(Double),使用 FMA 融合乘加。
与 SIMDIntrinsics.SumSquaresFma() 一样使用 4 路独立累加器来打断 FMA 的依赖链;归约顺序不同会带来 ULP 级差异。
| Name | Type | Description |
|---|---|---|
v1 | Double() | - |
v2 | Double() | - |
SUM(v1(i) * v2(i))
Single(), Single())点积(Single),使用 FMA 融合乘加并以 Double 累加。
Double())平方和:SUM(v(i) ^ 2),使用 FMA 融合乘加。
使用 4 路独立累加器:FMA 的延时约为 4 个周期,若只用一个累加器, 整个循环会被这条依赖链串行化,吞吐无法超过「1 条 FMA / 4 周期」; 4 路累加器让乱序执行可以同时保持多条 FMA 在飞,长数组上能拿到接近 3~4 倍的额外提升(这也是 SimdReduce.SumSquares() 采用同样策略的原因)。
归约顺序与单累加器版本不同,因此结果可能存在浮点末位(ULP)级差异。
Double, Double(), Double())AXPY:out(i) = alpha * x(i) + y(i),使用 FMA 融合乘加。
Double, Double(), Double())就地 AXPY:y(i) += alpha * x(i),使用 FMA 融合乘加。
这是矩阵分解/求解器里出现频率最高的一类 BLAS-1 操作(列消元、Gram-Schmidt 正交化、秩一更新的行部分),就地更新可以避免为每次迭代分配临时数组。
为什么不使用“末块与末尾重叠”的技巧:输入与输出共用同一块内存, 重叠部分会把已经更新过的元素再算一次,因此尾块退化为单通道逐元素计算。
Double(), Double(), Double())融合乘加:out(i) = v1(i) * v2(i) + acc(i)