我遇到了一个不寻常的情况。在与它无关的循环之前添加的一行代码会减慢循环速度。这是“简化形式”的样子。
我有一个原始的循环方法:
int[] array = new int[ElemCnt];
public int Sum()
{
int sum = 0;
for (int i = 0; i < array.Length; i++)
sum += array[i];
return sum;
}
然后将此方法修改如下:
long state = 1;
public int Sum2()
{
int sum = 0;
if (Interlocked.Read(ref state) == 0)
return sum;
for (int i = 0; i < array.Length; i++)
sum += array[i];
return sum;
}
那些。它只是添加了一个循环前检查。
假设这个检查的执行时间可以通过某个常数来估计是合乎逻辑的。第二种方法(结果最差)的执行时间可以估计T2 ≈ T + C为 随着数组长度的增长,显然这两种方法的执行时间将越来越难以区分()。TCT2 ≈ T
但是,这不会发生:
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19044.1766 (21H2)
Intel Core i5-4690 CPU 3.50GHz (Haswell), 1 CPU, 4 logical and 4 physical cores
.NET SDK=6.0.301
[Host] : .NET 6.0.6 (6.0.622.26707), X64 RyuJIT
DefaultJob : .NET 6.0.6 (6.0.622.26707), X64 RyuJIT
| 方法 | 元件 | 意思是 | 错误 | 标准差 | 比率 | 比率标准差 |
|---|---|---|---|---|---|---|
| 和 | 1000 | 590.1ns | 6.22ns | 5.81ns | 1.00 | 0.00 |
| 总和2 | 1000 | 1,896.4ns | 9.88ns | 9.24ns | 3.21 | 0.03 |
| 和 | 10000 | 5,729.5ns | 27.40ns | 24.29ns | 1.00 | 0.00 |
| 总和2 | 10000 | 18.929.2ns | 208.10ns | 194.66ns | 3.30 | 0.03 |
| 和 | 100000 | 57.273.8ns | 258.89ns | 242.17ns | 1.00 | 0.00 |
| 总和2 | 100000 | 187.707.1ns | 2,121.93ns | 1,984.85ns | 3.28 | 0.03 |
| 和 | 1000000 | 590.207.1ns | 9,395.29ns | 8,328.68ns | 1.00 | 0.00 |
| 总和2 | 1000000 | 1,943,186.9ns | 34.793.55ns | 32.545.90ns | 3.29 | 0.06 |
那些。它确实有效T2 ≈ K * T!
这表明循环在第二种情况下运行较慢。就好像添加的检查以某种方式减慢了循环。这是怎么回事?
基准代码:
public class LoopBench
{
int[] array = null;
long state = 1;
[Params(1000, 10000, 100000, 1000000)]
public int ElemCnt { get; set; }
[GlobalSetup]
public void Setup()
{
array = new int[ElemCnt];
}
[Benchmark(Baseline = true)]
public int Sum()
{
int sum = 0;
for (int i = 0; i < array.Length; i++)
sum += array[i];
return sum;
}
[Benchmark]
public int Sum2()
{
int sum = 0;
if (Interlocked.Read(ref this.state) == 0)
return sum;
for (int i = 0; i < array.Length; i++)
sum += array[i];
return sum;
}
}