我有一个关于在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”属性调用),然后另外30%用于get_Item和get_Value调用,然后是5%in方法AddCellValue(这是我的数据模型),剩下的时间被分散到各种方法调用中。
我有什么问题,或者这种表现是否正常?
干杯
答案 0 :(得分: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 :(得分:1)
在我看来,是的,观察到的性能对于EPPlus是正常的。五年后,我在EPPlus 4.5.2.1中遇到了类似的问题。分析给出了get_Worksheet中的59%,在i5-4200U上读取的单线程电子表格正在管理约120,000个单元/秒。尽管这比原始帖子中提到的每秒50,000个单元/秒有所改善,但很可能归因于硬件差异。
为进行比较,在看起来像i7-7700的SpreadsheetLight benchmarks 425,000 cells/second上,它比我为EPPlus测量的速度快大约三倍。我用C#编写的未经优化的自制软件解析器每秒读取大约430,000个单元格,并从.csv文件中检索相同的数据,而@Tim Andersen的SpreadsheetGear注释将其归一化为400,000个单元格/秒。我还没有在EPPlus和其他Excel库(例如ClosedXML,NPIO,Aspose或Microsoft的Open XML SDK)之间找到比较基准。
在EPPlus中,我介绍的方法从最快到最慢,
ExcelWorksheet.Cells[1, 1, dimension.Rows, dimension.Columns].Value
(本质上是@Kevin M的答案,但并没有一off而就)ExcelWorksheet.GetValue<string>(row, column)
ExcelWorksheet.GetValue(row, column)
ExcelWorksheet.Cells[row, column].Text
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快三倍。