You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 22:01:18