【问题标题】:C# load xlsx file performance badC# 加载 xlsx 文件性能不佳
【发布时间】:2013-05-22 13:14:00
【问题描述】:

我有一个关于在 C# 中加载 Excel (Xlsx) 文件的问题。我已经使用 NPOI 2.0 实现了 Excel 加载,但性能非常糟糕(15 到 25 秒的加载时间,10000 行和 60 列(在 Win7 上运行 Intel(R) Core(TM) i5-3210M CPU @ 2.50GHz(4 CPU),~2.5GHz))。我认为这是因为 NPOI 2.0 仍处于测试阶段,所以我尝试了另一个名为 EPPlus 的库,它仍然需要大约相同的时间来加载 Excel 文件。

这是我使用 EPPlus 加载它的方式:

var existingFile = new FileInfo(path);

var excelData = new ExcelViewModel(path);

// Open and read the XlSX file.
using (var package = new ExcelPackage(existingFile))
{
    // Get the work book in the file
    ExcelWorkbook workBook = package.Workbook;
    if (workBook != null)
    {
        // Here is some initializing......

        var viewSheetModel = new ExcelSheetViewModel(sheet.Name, numberOfColumns, titles);
        for (var row = titleRowIndex + 1; row <= end.Row; ++row)
        {
            var viewRowModel = new ExcelRowViewModel();

            for (int column = start.Column; column <= end.Column; ++column)
            {
                var cell = sheet.Cells[row, column];
                viewRowModel.AddCellValue(cell.Value != null ? cell.Value.ToString() : string.Empty);
            }

            viewSheetModel.Rows.Add(viewRowModel);
        }

        excelData.AddSheet(viewSheetModel);
    }
}

根据 dotTrace Profiler,大约 40% 的时间浪费在 get_Workbook 方法(通过访问“package.Workbook”属性调用)中,然后在 get_Item 和 get_Value 调用中浪费了另外 30%,然后在调用中浪费了 5% AddCellValue 方法(这是我的数据模型),其余时间分散到各种方法调用中。

是我做错了什么,还是这种表现正常?

干杯

【问题讨论】:

  • 我无法谈论上述性能,但您可以考虑查看像 SpreadsheetGear 这样的产品,它可以轻松处理这种文件大小。我不知道您的工作簿包含什么样的数据,但是在我的 Intel i7-3770K @ 3.50GHz 上加载一个包含 10K 行和 60 列随机文本/数字数据的工作簿并读取每个单元格值花费了 SpreadsheetGear ~1 秒。顺便说一句,我让 SpreadsheetGear 在大约相同的时间内生成这些测试工作簿。如果您愿意,我可以提供更多详细信息。免责声明:我为 SpreadsheetGear 工作。
  • 您使用的是哪个版本的 EPPlus?我尝试使用 EPPlus 3.1.3,它的加载速度足够快。没有实际的 Excel 文件,很难说更多。也许 Excel 文件有其他类型的“隐藏”数据(隐藏的行/列/图片、多个数据密集型工作表)?

标签: c# performance excel epplus npoi


【解决方案1】:

我发现 FOR 循环非常昂贵。以下是我如何在 1 秒多的时间内加载 85000 x 26 的纸张。

ExcelWorksheet ws = ...

Int32 maxLength = ws.Dimension.End.Row + 1;
Int32 maxWidth = ws.Dimension.End.Column + 1;

// Fetch the entire sheet as one huge range
ExcelRange cells = ws.Cells[1, 1, maxLength, maxWidth];

// cells.Values now contains a 2 dimensional object array
// Feel free to stop here

// I wanted a jagged array of type string, so I converted it.
// Start by converting the 2D array to 1D.
object[] obj_values = ((object[,]) cells.Value).Cast<object>().ToArray();

// Convert object[] to string[]
string[] str_values = Array.ConvertAll(obj_values, p => p == null ? "" : p.ToString());

// Chunk 1D array back into a jagged array and convert nulls to String.Empty
Int32 j = 0;
string[][] values = str_values.GroupBy(p => j++ / maxWidth).Select(q => q.ToArray()).ToArray();

// This was very fast compared to FOR loops!

【讨论】:

    【解决方案2】:

    在我看来,是的,EPPlus 观察到的性能是正常的。五年后,我在使用 EPPlus 4.5.2.1 时遇到了类似的问题。分析在 get_Worksheet 中提供了 59%,并且在 i5-4200U 上读取的单线程电子表格管理着大约 120,000 个单元/秒。虽然这比原帖中提到的大约 50,000 个细胞/秒有所改进,但很可能是硬件差异造成的。

    作为比较,SpreadsheetLight benchmarks 425,000 cells/second 似乎是 i7-7700,比我为 EPPlus 测量的速度快大约三倍。我用 C# 编写的自制未优化解析器每秒读取大约 430,000 个单元格,从 .csv 文件中检索相同的数据,上面的 @Tim Andersen 的 SpreadsheetGear 注释标准化为 400,000 个单元格/秒。我还没有找到 EPPlus 和其他 Excel 库(例如 ClosedXML、NPIO、Aspose 或 Microsoft 的 Open XML SDK)之间的比较基准。

    在 EPPlus 中,我描述的方法是,从最快到最慢,

    1. ExcelWorksheet.Cells[1, 1, dimension.Rows, dimension.Columns].Value(本质上是@Kevin M 的回答,但没有一个关闭)
    2. ExcelWorksheet.GetValue&lt;string&gt;(row, column)
    3. ExcelWorksheet.GetValue(row, column)
    4. ExcelWorksheet.Cells[row, column].Text
    5. ExcelWorksheet.Cells[row, column].Value

    从 EPPlus 4.5.2.1 开始,在第一种方法中从 ExcelRange.Value 获取 object[,] 比 GetValue() 重载快几个百分点。通过 Cells[row, column] 逐个单元格访问比 GetValue() 慢约 25%。

    EPPlus sources 的审查表明需要在 EPPlus 中更改代码以进行改进。在我分析过的所有路径上,工作簿访问仍然很昂贵,而且它是单线程的,阻止了额外内核的线性扩展。单元地址转换和对 System.Globalization 的可提升调用也带来了不小的开销,这与其他库一致,比 EPPlus 快大约三倍。

    【讨论】:

      猜你喜欢
      • 2021-08-12
      • 2011-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-12
      • 2015-07-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多