如何精准测量代码性能并降低抖动?
如何降低短代码性能测试中的抖动?
我正在研究如何测量代码段性能以最大化优化效果,测试时发现大量偏离平均值的异常值。以下代码用于测量一段运行时长约400ns的代码并统计异常值,在Windows 10笔记本(Intel CPU,已禁用超线程,.NET 6和.NET 7测试结果相近)上,约99.77%的结果低于600ns,请问该如何降低测试中的抖动?
测试代码
public static void Test() { int counter; int c = -1; int[] outliers = new int[20]; for (counter = 0; counter < 10000000; ++counter) { long startTime = Stopwatch.GetTimestamp(); TestFunction(Environment.TickCount, 1000); long endTime = Stopwatch.GetTimestamp() - startTime; endTime *= 100; switch (endTime) { case > 2000: outliers[0]++; break; case > 1900: outliers[1]++; break; case > 1800: outliers[2]++; break; case > 1700: outliers[3]++; break; case > 1600: outliers[4]++; break; case > 1500: outliers[5]++; break; case > 1400: outliers[6]++; break; case > 1300: outliers[7]++; break; case > 1200: outliers[8]++; break; case > 1100: outliers[9]++; break; case > 1000: outliers[10]++; break; case > 900: outliers[11]++; break; case > 800: outliers[12]++; break; case > 700: outliers[13]++; break; case > 600: outliers[14]++; break; case > 500: outliers[15]++; break; case > 400: outliers[16]++; break; case > 300: outliers[17]++; break; case > 200: outliers[18]++; break; case > 100: outliers[19]++; break; } } for (int i = 0; i < outliers.Length; ++i) { Console.WriteLine($"For timing > {2000 - i * 100} ns count:{outliers[i]}"); } } public static long TestFunction(long seed, int count) { long result = seed; for (int i = 0; i < count; ++i) { result ^= i ^ seed; // 无意义的位运算,模拟测试代码 } return result; }
测试结果
For timing > 2000 ns count:8563 For timing > 1900 ns count:200 For timing > 1800 ns count:276 For timing > 1700 ns count:522 For timing > 1600 ns count:433 For timing > 1500 ns count:538 For timing > 1400 ns count:1742 For timing > 1300 ns count:1110 For timing > 1200 ns count:4458 For timing > 1100 ns count:4853 For timing > 1000 ns count:19 For timing > 900 ns count:16 For timing > 800 ns count:13 For timing > 700 ns count:31 For timing > 600 ns count:354 For timing > 500 ns count:2395207 For timing > 400 ns count:7581665 For timing > 300 ns count:0 For timing > 200 ns count:0 For timing > 100 ns count:0
降低测试抖动的可行方案
1. 提前预热代码
短代码首次执行会触发JIT编译、缓存加载,导致耗时异常。正式测试前先执行若干次目标函数,让运行环境进入稳定状态:
// 在Test方法的循环前添加预热逻辑 for (int warmup = 0; warmup < 1000; warmup++) { TestFunction(Environment.TickCount, 1000); }
2. 隔离系统干扰
- 关闭后台无关程序(浏览器、杀毒软件、同步工具等),减少系统线程调度对测试的抢占。
- 将测试进程设为高优先级:打开任务管理器,找到测试进程,右键选择「设置优先级」→「高」(谨慎使用「实时」优先级,避免系统无响应)。
- 切换到高性能电源计划:
- 打开「电源选项」,选择「高性能」。
- 进入计划设置,将「处理器电源管理」下的「最小/最大处理器状态」均设为100%,确保CPU全程满频运行。
3. 优化测量逻辑
- 避免在测量循环内执行I/O操作(如Console.WriteLine),所有统计完成后再统一输出结果。
- 修正时间转换逻辑:当前用
*100转换纳秒不准确,需用Stopwatch.Frequency计算真实耗时:
long elapsedTicks = endTime - startTime; double elapsedNs = (elapsedTicks * 1_000_000_000.0) / Stopwatch.Frequency;
- 可尝试批量测量:连续执行N次
TestFunction后计算总耗时,再除以N得到单次耗时,降低Stopwatch调用的开销占比(注意批量次数不宜过多,避免缓存效应影响结果)。
4. 绑定CPU核心
将测试进程绑定到单个CPU核心,避免线程在核心间切换导致的缓存失效和调度延迟:
using System.Diagnostics; // 在Test方法开头添加核心绑定逻辑 Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // 绑定到第一个逻辑核心
5. 修正异常值统计逻辑
当前的switch case顺序会导致统计重复(如>2000ns的结果只会被计入第一个case,不会触发后续的>1900等判断),可改为区间统计:
// 替换原有的switch逻辑 double elapsedNs = (endTime * 1_000_000_000.0) / Stopwatch.Frequency; int bucket = (int)(elapsedNs / 100); // 超出最大区间的统一计入最后一个桶 if (bucket >= outliers.Length) { bucket = outliers.Length - 1; } outliers[bucket]++;
这样能准确统计每个100ns区间的执行次数,结果更具参考性。
内容的提问来源于stack exchange,提问作者tomatoRadar
相关产品推荐
相关产品推荐

