C#加载xlsx文件性能不好

问题描述 投票:2回答:2

我有一个关于在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%,然后在AddCellValue方法中调用5% (这是我的数据模型),其余的时间被分散到各种方法调用中。

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

干杯

c# performance excel epplus npoi
2个回答
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!

1
投票

在我看来,是的,观察到的表现对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<string>(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获取对象[,]比GetValue()重载快几个百分点。通过Cells [行,列]逐个单元格访问比GetValue()慢大约25%。

回顾EPPlus sources建议改进EPPlus中的代码更改。在我所描述的所有路径上,工作簿访问仍然很昂贵,并且它是单线程的,阻止了来自其他核心的线性扩展。从单元格地址转换和可升级调用到System.Globalization也是非常重要的开销,这与其他库大约比EPPlus快三倍。

© www.soinside.com 2019 - 2024. All rights reserved.