发布于2026-07-23 阅读(0)
扫一扫,手机访问
SIMD(Single Instruction, Multiple Data)——单指令多数据,听起来像是个很硬核的概念,但其实它就在我们身边。现代CPU几乎都内置了SIMD指令集,比如Intel的SSE、A VX,ARM的NEON。简单说,就是让一条指令同时处理多个数据,而不是挨个儿来。这就像你有一排灯泡,传统做法是一个一个拧,SIMD则是整排一起亮。那么,在.NET里怎么用上这个能力?性能又能提升多少?下面从最基础的概念讲起,逐步深入到实际代码。
SIMD(Single Instruction, Multiple Data) 译为 单指令多数据,是一种并行计算技术,允许单条指令同时对多个数据元素进行操作,从而提高计算效率。
与 SIMD 相对的是 SISD(Single Instruction, Single Data,单指令单数据),即每条指令只处理一个数据元素。
现在的大多数 CPU 都支持 SIMD 指令集,例如 Intel 的 SSE 和 A VX,ARM 的 NEON 等。
如果我们要对两组数组进行加法运算,传统方法(SISD)是逐个元素相加,而使用 SIMD 技术,可以一次性将多个元素加载到向量寄存器中,并执行单一的加法指令,从而显著提高计算效率。

下面我们通过一个简单的示例,对比传统的数组加法和使用 SIMD 优化后的数组加法在性能上的差异。例子中会对两个浮点数组进行加法运算,把结果存储在第三个数组中。
using System.Runtime.Intrinsics;using BenchmarkDotNet.Attributes;using BenchmarkDotNet.Running;[MemoryDiagnoser]public class SimdBenchmark{ private float[] _arrA; private float[] _arrB; private float[] _resultArray; private readonly int _dataSize = 1_000_000; [GlobalSetup] public void Setup() { var random = new Random(); _arrA = new float[_dataSize]; _arrB = new float[_dataSize]; _resultArray = new float[_dataSize]; for (int i = 0; i < _dataSize; i++) { _arrA[i] = (float)random.NextDouble() * 10f; _arrB[i] = (float)random.NextDouble() * 10f; } } [Benchmark] public void NormalAdd() { for (int i = 0; i < _dataSize; i++) { _resultArray[i] = _arrA[i] + _arrB[i]; } } [Benchmark] public void SimdAdd() { // 每次处理 4 个元素 int simdLength = Vector128.Count; // 4 int i = 0; // 处理可被 SIMD 整除的部分 for (; i <= _dataSize - simdLength; i += simdLength) { var va = Vector128.Create(_arrA[i], _arrA[i + 1], _arrA[i + 2], _arrA[i + 3]); var vb = Vector128.Create(_arrB[i], _arrB[i + 1], _arrB[i + 2], _arrB[i + 3]); (va + vb).CopyTo(_resultArray, i); } // 处理尾部不足 4 个的元素 for (; i < _dataSize; i++) { _resultArray[i] = _arrA[i] + _arrB[i]; } }}public class Program{ public static void Main(string[] args) { BenchmarkRunner.Run(); }}
BenchmarkDotNet v0.15.6, macOS Sequoia 15.7.2 (24G325) [Darwin 24.6.0]Apple M2 Max, 1 CPU, 12 logical and 12 physical cores.NET SDK 9.0.100 [Host] : .NET 9.0.0 (9.0.0, 9.0.24.52809), Arm64 RyuJIT armv8.0-a DefaultJob : .NET 9.0.0 (9.0.0, 9.0.24.52809), Arm64 RyuJIT armv8.0-a| Method | Mean | Error | StdDev | Allocated ||---------- |---------:|--------:|--------:|----------:|| NormalAdd | 880.4 us | 9.28 us | 7.75 us | - || SimdAdd | 568.5 us | 4.18 us | 3.70 us | - |
在 MacBook Pro M2 Max 上测试,使用 SIMD 优化后的数组加法运算相比传统方法提升了约 35% 的性能。需要说明的是,这个例子中结果需要拷贝到目标数组,如果能在向量上直接做更多计算,性能提升会更明显。代码在 Windows 和 Linux 上同样可以跑,读者不妨自己测测不同平台的差异。
.NET 提供了三个命名空间来使用 SIMD:
System.Runtime.Intrinsics 中定义了表示不同大小向量的结构体,以及相应的静态操作类。
结构体
| 类型 | 描述 |
|---|---|
| Vector64 | 64 位向量,用于低级别并行优化。 |
| Vector128 | 128 位向量,最常用的尺寸。 |
| Vector256 | 256 位向量。 |
| Vector512 | 512 位向量。 |
静态类
| 类型 | 描述 |
|---|---|
| Vector64 | 64 位向量的创建与操作。 |
| Vector128 | 128 位向量的创建与操作。 |
| Vector256 | 256 位向量的创建与操作。 |
| Vector512 | 512 位向量的创建与操作。 |
而 System.Runtime.Intrinsics.X86 和 System.Runtime.Intrinsics.Arm 则提供了对应硬件指令集的封装,比如 Sse、A vx、AdvSimd 等。下面列出几个常见的类:
| 类型 | 描述 |
|---|---|
| Sse | x86/x64 SSE 指令集 |
| Sse2 | SSE2 指令集 |
| A vx | A VX 指令集 |
| A vx2 | A VX2 指令集 |
| AdvSimd | ARM Advanced SIMD (NEON) |
更完整的信息可以查阅官方文档:System.Runtime.Intrinsics.X86 命名空间 和 System.Runtime.Intrinsics.Arm 命名空间。
向量的“大小”指的是寄存器能容纳的总位数。比如 128 位的向量,可以存 4 个 32 位浮点数(128/32=4),也可以存 2 个 64 位双精度浮点数(128/64=2)。很好理解:
using System.Runtime.Intrinsics;// 128位向量,16个字节Vector128vectorByte = Vector128.Create((byte)1, (byte)2, (byte)3, (byte)4, (byte)5, (byte)6, (byte)7, (byte)8, (byte)9, (byte)10, (byte)11, (byte)12, (byte)13, (byte)14, (byte)15, (byte)16);// 128位向量,4个浮点数Vector128 vectorFloat = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f);// 256位向量,8个浮点数Vector256 vector256Float = Vector256.Create(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f);// 128位向量,2个双精度浮点数Vector128 vectorDouble = Vector128.Create(1.0, 2.0);// 256位向量,4个双精度浮点数Vector256 vector256Double = Vector256.Create(1.0, 2.0, 3.0, 4.0);
.NET 的 SIMD 是跨平台的。无论是 x86 还是 ARM,运行时都会自动选择最合适的硬件指令集来执行。你可以用 VectorXXX.IsHardwareAccelerated 检查当前平台是否支持特定大小的向量操作:
Console.WriteLine(Vector128.IsHardwareAccelerated ? "128 位向量操作受支持" : "128 位向量操作不受支持");
但即便硬件不支持,.NET 也会自动回退到非 SIMD 的实现,保证代码兼容性。
向量运算既可以用静态方法如 Vector128.Add,也可以直接用 +、-、*、/ 等运算符——结构体重载了这些符号,写起来很直观:
using System.Runtime.Intrinsics;Vector128vectorA = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f);Vector128 vectorB = Vector128.Create(5.0f, 6.0f, 7.0f, 8.0f);var result = Vector128.Add(vectorA, vectorB);Console.WriteLine($"Result: {result}");
Result: <6, 8, 10, 12>
使用特定指令集之前,通常需要检查平台是否支持,通过 IsSupported 属性来判断:
using System.Runtime.Intrinsics.X86;Console.WriteLine(Sse.IsSupported ? "SSE 指令集受支持" : "SSE 指令集不受支持");
确认支持后,就可以调用指令集类提供的静态方法了。例如用 Sse.Add 做加法:
using System.Runtime.Intrinsics;using System.Runtime.Intrinsics.X86;if (Sse.IsSupported){ Vector128 vectorA = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f); Vector128 vectorB = Vector128.Create(5.0f, 6.0f, 7.0f, 8.0f); var result = Sse.Add(vectorA, vectorB); Console.WriteLine($"Result: {result}");}else{ Console.WriteLine("SSE 指令集不受支持");}
Result: <6, 8, 10, 12>
ARM 平台则用 AdvSimd:
using System.Runtime.Intrinsics;using System.Runtime.Intrinsics.Arm;if (AdvSimd.IsSupported){ Vector128 vectorA = Vector128.Create(1.0f, 2.0f, 3.0f, 4.0f); Vector128 vectorB = Vector128.Create(5.0f, 6.0f, 7.0f, 8.0f); var result = AdvSimd.Add(vectorA, vectorB); Console.WriteLine($"Result: {result}");}else{ Console.WriteLine("AdvSimd 指令集不受支持");}
Result: <6, 8, 10, 12>
除了底层 Intrinsics,.NET 在 System.Numerics 里提供了更高级的封装,比如 Vector 和 Matrix4x4,让 SIMD 编程更简单。
Vector 是一个通用向量类型,支持多种数值类型,并且会自动根据硬件选择最佳向量大小(128位或256位),真正做到跨平台优化。下面是用它做数组加法的例子:
var vectorSize = Vector.Count;float[] arrayA = new float[1000];float[] arrayB = new float[1000];float[] resultArray = new float[1000];for (int i = 0; i <= arrayA.Length - vectorSize; i += vectorSize){ var va = new Vector (arrayA, i); var vb = new Vector (arrayB, i); (va + vb).CopyTo(resultArray, i);}
这些结构体分别表示二维、三维、四维向量,常用于图形和物理计算。内部实现已经用上了 SIMD:
using System.Numerics;Vector3 vector1 = new Vector3(1.0f, 2.0f, 3.0f);Vector3 vector2 = new Vector3(4.0f, 5.0f, 6.0f);Vector3 resultAdd = Vector3.Add(vector1, vector2);Console.WriteLine($"Addition: {resultAdd}"); // <5, 7, 9>float dotProduct = Vector3.Dot(vector1, vector2);Console.WriteLine($"Dot Product: {dotProduct}"); // 32Vector3 normalized = Vector3.Normalize(vector1);Console.WriteLine($"Normalized: {normalized}"); // <0.2672612, 0.5345225, 0.8017837>
打开 Vector3 的源码看看,会发现它内部就是用 Vector128.Create 实现的:
public struct Vector3 : IEquatable, IFormattable{ public float X; public float Y; public float Z; public Vector3(float x, float y, float z) => this = Vector3.Create(x, y, z); public static Vector3 Create(float x, float y, float z) { return Vector128.Create(x, y, z, 0.0f).AsVector3(); } // 其他成员...}
这些矩阵结构体同样内置了 SIMD 优化,用于变换和投影:
using System.Numerics;Matrix4x4 matrix = Matrix4x4.CreateRotationX((float)(Math.PI / 4));Vector3 point = new Vector3(1.0f, 0.0f, 0.0f);Vector3 transformedPoint = Vector3.Transform(point, matrix);Console.WriteLine($"Transformed Point: {transformedPoint}");
Transformed Point: <1, 0, 0>
ASCII 码表中,大小写字母的差异仅在第 6 位(值为 0x20)。大写字母该位为 0,小写为 1。因此用异或操作就能轻松转换:
Console.WriteLine((char)('a' ^ 0x20)); // 输出 AConsole.WriteLine((char)('A' ^ 0x20)); // 输出 a
查看 ASCII 表对照:
| 字符 | 十进制 | 十六进制 | 二进制 |
|---|---|---|---|
| A | 65 | 0x41 | 01000001 |
| B | 66 | 0x42 | 01000010 |
| C | 67 | 0x43 | 01000011 |
| D | 68 | 0x44 | 01000100 |
| a | 97 | 0x61 | 01100001 |
| b | 98 | 0x62 | 01100010 |
| c | 99 | 0x63 | 01100011 |
| d | 100 | 0x64 | 01100100 |
.NET 的 System.Text.AsciiToUpper、ToLower 等方法,底层就是利用 SIMD 加速的。下面是一个简化版的核心实现,展示了如何用向量判断小写字母范围并转换:
void ToUpperInPlace(Spanvalue){ var buffer = MemoryMarshal.Cast (value); var elementCount = (uint)buffer.Length; var numElementsPerVector = (uint)(Unsafe.SizeOf >() / sizeof(ushort)); if (Vector128.IsHardwareAccelerated && elementCount >= numElementsPerVector) { ushort sourceSignedMinValue = (ushort)(1 << (8 * sizeof(ushort) - 1)); var subtractionVector = Vector128.Create((ushort)(sourceSignedMinValue + 'a')); var comparisonVector = Vector128.Create((ushort)(sourceSignedMinValue + 26)); var caseConversionVector = Vector128.Create((ushort)0x20); uint i = 0; uint n = elementCount - (elementCount % numElementsPerVector); for (; i < n; i += numElementsPerVector) { var srcVector = Vector128.LoadUnsafe(ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), (int)i)); var matches = SignedLessThan(srcVector - subtractionVector, comparisonVector); srcVector ^= matches & caseConversionVector; srcVector.StoreUnsafe(ref Unsafe.Add(ref MemoryMarshal.GetReference(buffer), (int)i)); } for (; i < elementCount; i++) { ushort c = Unsafe.Add(ref MemoryMarshal.GetReference(buffer), (int)i); if (c is >= 'a' and <= 'z') { c = (ushort)(c - 0x20); } Unsafe.Add(ref MemoryMarshal.GetReference(buffer), (int)i) = c; } } else { for (int i = 0; i < buffer.Length; i++) { ushort c = buffer[i]; if (c is >= 'a' and <= 'z') { c = (ushort)(c - 0x20); } buffer[i] = c; } }}Vector128 SignedLessThan(Vector128 left, Vector128 right){ return Vector128.LessThan(left.AsInt16(), right.AsInt16()).AsInt16().AsUInt16();}
调用示例:
string input = "Hello World! This is a Test String.";Spanp = input.ToCharArray();ToUpperInPlace(p);string result = new string(p);Console.WriteLine(result); // "HELLO WORLD! THIS IS A TEST STRING."
需要说明的是,实际 Ascii 类要复杂得多,包含边界检查和错误处理,上述代码只是核心思路的简化版。日常使用直接调 Ascii.ToUpperInPlace 即可:
using System.Text;string input = "Hello World! This is a Test String. 这部分不会被转换。";Spanp = input.ToCharArray();Ascii.ToUpperInPlace(p, out int charsWritten);string result = new string(p[..charsWritten]);Console.WriteLine(result); // "HELLO WORLD! THIS IS A TEST STRING."
SIMD 也适合大批量二进制数据的位操作。比如 BinaryPrimitives.ReverseEndianness 就利用 SIMD 高效反转字节序:
Spandata = [0x1234, 0xABCD, 0x5678, 0xEF01];Span reversedData = stackalloc ushort[4];BinaryPrimitives.ReverseEndianness(data, reversedData);foreach (var value in reversedData){ Console.WriteLine(value.ToString("X4"));}
3412CDAB785601EF
SIMD 在 .NET 中提供了强大的并行计算能力,能显著提升大数据量处理的性能。通过 System.Runtime.Intrinsics 和 System.Numerics,我们可以方便地利用 SIMD 指令进行高效的向量化运算。很多基础库已经内置了 SIMD 优化,日常开发中通过它们就能间接受益,无需深入了解底层细节。当然,对于性能敏感的应用,直接使用 SIMD 指令集仍然是非常值得掌握的技能。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8