Jan*_*kke 6 c# arrays performance benchmarking
| Method | Mean | Error | StdDev |
|--------------- |---------:|---------:|---------:|
| ArrayRefIndex | 661.9 us | 12.95 us | 15.42 us |
| ArraySpanIndex | 640.4 us | 4.08 us | 3.82 us |
Run Code Online (Sandbox Code Playgroud)
为什么循环array.AsSpan()
比直接循环源数组更快?
public struct Struct16
{
public int A;
public int B;
public int C;
public int D;
}
public class Program
{
public const int COUNT = 100000;
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<Program>();
}
[Benchmark]
public int ArrayRefIndex()
{
Struct16[] myArray = new Struct16[COUNT];
int sum = 0;
for (int i = 0; i < myArray.Length; i++)
{
ref Struct16 value = ref myArray[i];
sum += value.A = value.A + value.B + value.C + value.D;
}
return sum;
}
[Benchmark]
public int ArraySpanIndex()
{
Struct16[] myArray = new Struct16[COUNT];
int sum = 0;
Span<Struct16> span = myArray.AsSpan();
for (int i = 0; i < span.Length; i++)
{
ref Struct16 value = ref span[i];
sum += value.A = value.A + value.B + value.C + value.D;
}
return sum;
}
}
Run Code Online (Sandbox Code Playgroud)
小智 6
简答
Span 保证“任意内存的连续区域”,这允许编译器对 CLI 指令进行优化。
长答案
如果您在反汇编中打开提供的代码(调试 -> Windows -> 反汇编),您将在 ArrayRefIndex() 中找到以下内容
ref Struct16 value = ref myArray[i];
00007FFC3E860DCC movsxd r8,ecx
00007FFC3E860DCF shl r8,4
00007FFC3E860DD3 lea r8,[rax+r8+10h] // <----
Run Code Online (Sandbox Code Playgroud)
“lea”代表加载有效地址。这意味着 ArrayRefIndex 函数速度较慢,因为它将 struct array 视为无序内存。
当我们查看 ArraySpanIndex 时,我们可以看到它没有“lea”指令,而是仅替换为“add”。我没有确认,但这可能只是添加下一个内存位置的结构长度。不管怎样,“lea”指令是两个函数之间唯一的增量,将罪魁祸首缩小到了时间差。
ref Struct16 value = ref span[i];
00007FFC3E8613FA movsxd r8,ecx
00007FFC3E8613FD shl r8,4
00007FFC3E861401 add r8,rax // <----
Run Code Online (Sandbox Code Playgroud)