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

C#读取CSV时while循环内外构建字典为何存在显著性能差异

CSV读取流程中字典构建位置导致的稳定性能差异问题

问题现象

读取大型CSV文件时存在可稳定复现的性能差:

  • 若在文件读取的while循环内同步完成字典分组构建,全流程耗时约4.1秒
  • 若将字典分组逻辑移出文件读取循环,先把所有解析完成的Stock对象存入临时列表,再单独遍历列表构建字典,全流程耗时仅3.4秒
    两个版本逻辑、最终生成的数据结构完全一致,多次重复测试时间差始终稳定存在。

相关实现代码

数据类定义

public class Stock
{
    public string StockDate { get; set; }
    public string StockID { get; set; }
    public string StockName { get; set; }
    public string SecBrokerID { get; set; }
    public string SecBrokerName { get; set; }
    public decimal Price { get; set; }
    public long BuyQty{ get; set; }
    public long SellQty { get; set; }

    public Stock(string s)
    {
        string[] data = s.Split(',');
        StockDate = data[0];
        StockID = data[1];
        StockName = data[2];
        SecBrokerID = data[3];
        SecBrokerName = data[4];
        Price = decimal.Parse(data[5]);
        BuyQty = long.Parse(data[6]);
        SellQty = long.Parse(data[7]);
    }
}

慢版本实现(循环内同步构建字典,~4.1s)

private void ReadFileWorkerRun(object sender, EventArgs e)
{
    List<Stock> lineTemp = new List<Stock>();
    List<Stock> allStock = new List<Stock>();
    List<List<Stock>> orderedAll = new List<List<Stock>>();
    Categories = new Dictionary<string, List<Stock>>() { { GlobalVariable.ALL, allStock } };
    DictionaryOrder = new List<(string, string)>();

    using (StreamReader lines = new StreamReader(FilePath))
    {
        string line = lines.ReadLine();

        // 读循环内直接完成字典分组
        while ((line = lines.ReadLine()) != null)
        {
            Stock temp = new Stock(line);

            if (!Categories.TryGetValue(temp.StockID, out List<Stock> targetList))
            {
                targetList = new List<Stock>();
                orderedAll.Add(targetList);
                Categories.Add(temp.StockID, targetList);
                DictionaryOrder.Add((temp.StockID, temp.StockName));
            }
            targetList.Add(temp);
        }
    }

    foreach (List<Stock> stockList in orderedAll)
    {
        allStock.AddRange(stockList);
    }
}

快版本实现(分阶段处理,~3.4s)

private void ReadFileWorkerRun(object sender, EventArgs e)
{
    List<Stock> lineTemp = new List<Stock>();
    List<Stock> allStock = new List<Stock>();
    List<List<Stock>> orderedAll = new List<List<Stock>>();
    Categories = new Dictionary<string, List<Stock>>() { { GlobalVariable.ALL, allStock } };
    DictionaryOrder = new List<(string, string)>();

    using (StreamReader lines = new StreamReader(FilePath))
    {
        string line = lines.ReadLine();
        // 第一阶段:仅完成读文件+对象解析,存入临时列表
        while ((line = lines.ReadLine()) != null)
        {
            lineTemp.Add(new Stock(line));
        }
    }

    // 第二阶段:遍历临时列表完成字典分组
    foreach (Stock temp in lineTemp)
    {
        if (!Categories.TryGetValue(temp.StockID, out List<Stock> targetList))
        {
            targetList = new List<Stock>();
            orderedAll.Add(targetList);
            Categories.Add(temp.StockID, targetList);
            DictionaryOrder.Add((temp.StockID, temp.StockName));
        }
        targetList.Add(temp);
    }

    foreach (List<Stock> stockList in orderedAll)
    {
        allStock.AddRange(stockList);
    }
}

性能差异核心原因

这个差距和逻辑正确性无关,完全是运行时底层的执行效率差异导致的,核心影响因素有三个:

  • 操作耦合打断CPU与IO优化:慢版本把三类完全异构的操作塞进了同一个紧循环:阻塞式磁盘IO读行、CSV字符串解析/对象初始化、字典哈希计算/多列表写入。磁盘IO本身存在不确定的等待延迟,和CPU密集的字典、集合操作混跑时,会打断CPU的指令流水线调度,同时两类操作产生的临时内存(读行产生的字符串、集合扩容产生的旧数组)交叉触发GC,GC扫描、回收的暂停时间更长。
  • CPU缓存命中率差距:快版本的两个循环都是访问模式高度统一的纯操作:第一阶段循环只往连续内存的lineTemp里追加Stock对象,CPU可以提前把List内部数组、文件流缓冲区预读到L1/L2高速缓存;第二阶段遍历lineTemp时,遍历的内存地址是连续的,缓存命中率极高。而慢版本每次循环都要在文件流缓冲区、字典哈希桶结构、多个分散的分组List之间跳转访问,属于典型的随机内存访问,大量时间浪费在缓存未命中的内存读取等待上——这部分是两个版本差距的最主要来源。
  • JIT优化空间差异:快版本的两个循环逻辑单一、涉及的操作类型少,JIT编译器可以很容易做循环展开、方法内联、数组边界检查消除等优化;慢版本循环内分支多、操作对象类型杂,JIT无法做激进优化,很多运行时类型检查、边界检查会被保留,额外增加了执行开销。

通过性能探查工具查看两个版本的CPU缓存未命中计数、GC暂停时间,可以看到快版本的这两项指标都明显低于慢版本。


内容的提问来源于stack exchange,提问作者謝康豪

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:33:31