IOPS与FLOPS计算测试结果异常排查及代码优化咨询
IOPS/FLOPS测试代码问题分析与优化方案
我需要在总时长5分钟的测试中,分别运行150秒计算IOPS和FLOPS,但得到了异常测试结果(例如16线程下平均IOPS为53986225,标准差为1282739)。以下是测试代码,请分析问题并给出优化方案:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; namespace Threads { class Program { // Count of operations done per thread private static long[] operations; // Volatile variable to signal threads to stop private static volatile bool stop = false; private const float TestTimeInMinutes = 1f; private const float SleepInSeconds = 1f; // Handles used by threads to signal to it's caller it's done private static ManualResetEvent[] handles; static void Main(string[] args) { // Get optimal number of threads var tNum = Environment.ProcessorCount; // Ask user for thread count while (true) { Console.Write($"Please enter number of threads or 0 for default {tNum}: "); var input = Console.ReadLine(); if (int.TryParse(input, out var _tNum)) { if (_tNum != 0) { tNum = _tNum; } break; } } // Initialize operations array operations = new long[tNum]; // Initialize handles array handles = new ManualResetEvent[tNum]; // Populate handles for (var n = 0; n < tNum; n++) { handles[n] = new ManualResetEvent(false); } long totalIopS = 0; long totalFlopS = 0; long IopDev = 0; long FlopDev = 0; // Stopwatch to measure time var stopwatch = new Stopwatch(); for (int i = 0; i < 5; i++) { Console.WriteLine($"Test {i+1}"); stopwatch.Restart(); var iop = MeasureIOP(tNum); Console.WriteLine($"IOPS: {iop/30}\nElapsed: {stopwatch.Elapsed:mm\\:ss\\.fff}"); totalIopS += iop; IopDev += StandardDeviation(operations); stopwatch.Restart(); var flop = MeasureFLOP(tNum); Console.WriteLine($"FLOPS: {flop/30}\nElapsed: {stopwatch.Elapsed:mm\\:ss\\.fff}"); totalFlopS += flop; FlopDev += StandardDeviation(operations); } Console.WriteLine($"Average\nIOPS: {totalIopS/150}\nIOP Standard Deviation: {(int)(IopDev/150)}\nFLOP: {totalFlopS/150}\nFLOP Standard Deviation: {(int)(FlopDev/150)}"); Console.Write("The Test has ended"); var finalInput = Console.ReadLine(); } private static void FloatOp(object index) { float floatOp = 0; var i = (int) index; long op = 0; while (!stop) { floatOp = (floatOp + 0.8f)-0.10f*0.2f; op++; } operations[i] = op; // Signal that the thread is done handles[i].Set(); } private static long MeasureFLOP(int tNum) { const int numberOfTries = (int) ((TestTimeInMinutes / 2 * 60f) / SleepInSeconds); long flop = 0; for (var i = 0; i < numberOfTries; i++) { stop = false; for (var threadIndex = 0; threadIndex < tNum; threadIndex++) { // Reset the handle // By resetting the handle, the thread is considered active handles[threadIndex].Reset(); // Start a thread ThreadPool.QueueUserWorkItem(FloatOp, threadIndex); } Thread.Sleep((int)(SleepInSeconds*1000)); stop = true; // Wait for all threads to exit WaitHandle.WaitAll(handles); // Calculate flops flop += (int)((operations.Sum()/tNum)/SleepInSeconds); } return flop / numberOfTries; } private static void IntOp(object index) { long intop = 0; var i = (int) index; long op = 0; while (!stop) { intop = intop + 1 - 10 * 2; op++; } operations[i] = op; // Signal that the thread is done handles[i].Set(); } private static long MeasureIOP(int tNum) { const int numberOfTries = (int) ((TestTimeInMinutes / 2 * 60f) / SleepInSeconds); long iop = 0; for (var i = 0; i < numberOfTries; i++) { stop = false; for (var threadIndex = 0; threadIndex < tNum; threadIndex++) { // Reset the handle // By resetting the handle, the thread is considered active handles[threadIndex].Reset(); ThreadPool.QueueUserWorkItem(IntOp, threadIndex); } Thread.Sleep((int)(SleepInSeconds*1000)); stop = true; // Wait for all threads to exit WaitHandle.WaitAll(handles); // Calculate iop iop += (int)((operations.Sum()/tNum)/SleepInSeconds); } return iop / numberOfTries; } private static long StandardDeviation(long[] nums) { var average = nums.Average(); var sum = nums.Sum(n => Math.Pow(n - average, 2)); return (long)Math.Sqrt(sum / nums.Length); } } }
代码存在的问题
1. 概念混淆与计算逻辑错误
- 代码中
IntOp测试的是CPU整数运算,并非真实的IO(输入输出)操作,却命名为IOPS,概念完全混淆,测试结果无参考意义。 - IOPS/FLOPS计算逻辑错误:
MeasureIOP中operations.Sum()/tNum是单线程平均操作数,后续又在Main中除以30,最终结果被错误缩小,且未乘以线程数得到总运算量,导致数值异常。 - 标准差计算逻辑错误:直接累加每次测试的标准差再除以次数是数学错误,标准差不能通过这种方式求平均。
2. 编译器优化导致结果失真
IntOp和FloatOp中的循环操作过于简单且无实际副作用:intop = intop +1 -10*2可简化为intop = intop -19,JIT编译器会将此类无意义的循环优化为空操作或极快的循环,导致op计数异常高,完全无法反映真实运算性能。
3. 计时精度不足
使用Thread.Sleep(1000)控制测试时长,该方法精度极低(误差可达几十毫秒),导致实际测试时间与预期不符,影响结果准确性。
4. 线程管理与信号问题
- 复用
ManualResetEvent和线程池线程,线程池的调度延迟可能影响测试启动时机,导致计时偏差。 stop变量虽为volatile,但循环中频繁读取仍存在优化风险,线程停止后未立即退出的情况可能导致计数多算。
5. 测试时长与需求不符
代码中TestTimeInMinutes设为1分钟,虽总时长凑够5分钟,但每次测试的运算量统计逻辑错误,未满足“分别运行150秒计算”的需求。
优化方案
1. 修正概念与计算逻辑
- 若测试CPU整数运算,将命名改为
IPS(Integer Operations Per Second);若需真实IOPS测试,需替换为磁盘/网络读写操作(如创建临时文件进行随机读写)。 - 修正运算量计算:总每秒运算量 = 所有线程操作数总和 / 测试时间(秒),去掉多余的除以线程数和除以30的错误逻辑。
- 修正标准差计算:收集所有测试周期中每个线程的操作数,最后统一计算整体标准差。
2. 防止编译器优化
在循环操作中添加无法被优化的副作用,示例修改后的IntOp:
private static volatile long dummyInt; // 添加全局volatile变量 private static void IntOp(object index) { long intop = 0; var i = (int)index; long op = 0; while (!stop) { intop = intop + 1 - 10 * 2; dummyInt = intop; // 添加副作用,防止优化 op++; } operations[i] = op; handles[i].Set(); }
3. 提升计时精度
使用Stopwatch精确控制测试时长,替代Thread.Sleep:
// 在MeasureIOP中替换Thread.Sleep逻辑 var testStopwatch = Stopwatch.StartNew(); while (testStopwatch.Elapsed.TotalSeconds < SleepInSeconds) { Thread.Yield(); // 让出CPU,避免空循环占用资源 } stop = true;
4. 优化线程管理
使用Task替代ThreadPool.QueueUserWorkItem,更简洁可控:
// 替换MeasureIOP中的线程启动逻辑 var tasks = new Task[tNum]; for (var threadIndex = 0; threadIndex < tNum; threadIndex++) { var idx = threadIndex; tasks[idx] = Task.Run(() => IntOp(idx)); } // 等待所有任务完成 Task.WaitAll(tasks);
5. 修正测试时长配置
调整参数确保IOPS/FLOPS各跑150秒:
private const float SingleTestTimeSeconds = 150f;
直接运行一次150秒的测试,减少线程创建销毁的开销。
6. 优化结果统计
收集所有测试数据后再计算平均值和标准差:
// 在Main中收集所有线程操作数 List<long> allIopData = new List<long>(); List<long> allFlopData = new List<long>(); // 每次测试后添加数据 allIopData.AddRange(operations); // 最后计算整体平均值和标准差 double avgIops = allIopData.Sum() / SingleTestTimeSeconds; double iopStdDev = CalculateStandardDeviation(allIopData);
内容的提问来源于stack exchange,提问作者lishiyu
相关产品推荐
相关产品推荐

