优化数据处理方法的性能

时间:2015-07-14 19:17:49

标签: c# excel epplus

我使用以下代码从.txt文件中获取一些数据(格式为XML - 格式不正确),然后在进行一些处理后使用EPPlus将其写入.xlsxStreamElements基本上是经过修改的XmlReader。我的问题是关于性能,我做了一些改变,但没看到我还能做些什么。我将把它用于大型数据集,所以我试图修改以使其尽可能高效和快速。任何帮助将不胜感激!

我尝试使用p.SaveAs()来做excel写作,但它并没有真正看到性能差异。有更好的更快的写作方式吗?欢迎任何建议。

using (ExcelPackage p = new ExcelPackage())
    {
    ExcelWorksheet ws = p.Workbook.Worksheets[1];
    ws.Name = "data1";
    int rowIndex = 1; int colIndex = 1;

      foreach (var element in StreamElements(pa, "XML"))
      {
         var values = element.DescendantNodes().OfType<XText>()
         .Select(v => Regex.Replace(v.Value, "\\s+", " "));
         string[] data = string.Join(",", values).Split(',');

         data[2] = toDateTime(data[2]);

         for (int i = 0; i < data.Count(); i++)
         {
           if (rowIndex < 1000000) 
           { 
           var cell1 = ws.Cells[rowIndex, colIndex];
           cell1.Value = data[i];
           colIndex++;
           }
         }
         rowIndex++;
      }
    }

    ws.Cells[ws.Dimension.Address].AutoFitColumns();

    Byte[] bin = p.GetAsByteArray();
    using (FileStream fs = File.OpenWrite("C:\\test.xlsx"))
    {
      fs.Write(bin, 0, bin.Length);
    }

  }
}

目前,要进行处理,然后在Excel工作表中写入1百万行,大约需要30-35分钟。

1 个答案:

答案 0 :(得分:0)

之前我遇到过这个问题,当你逐个单独修改工作表单元格时,excel会有巨大的开销。

解决方法是创建一个对象数组并使用WriteRange功能填充工作表。

using(ExcelPackage p = new ExcelPackage()) {
    ExcelWorksheet ws = p.Workbook.Worksheets[1];
    ws.Name = "data1";

    //Starting cell
    int startRow = 1;
    int startCol = 1;

    //Needed for 2D object array later on
    int maxColCount = 0;
    int maxRowCount = 0;

    //Queue data
    Queue<string[]> dataQueue = new Queue<string[]>();

    //Tried not to touch this part
    foreach(var element in StreamElements(pa, "XML")) {
        var values = element.DescendantNodes().OfType<XText>()
            .Select(v = > Regex.Replace(v.Value, "\\s+", " "));

        //Removed unnecessary split and join, use ToArray instead
        string[] eData = values.ToArray();
        eData[2] = toDateTime(eData[2]);

        //Push the data to queue and increment counters (if needed)
        dataQueue.Enqueue(eData);

        if(eData.Length > maxColCount)
            maxColCount = eData.Length;

        maxRowCount++;
    }

    //We now have the dimensions needed for our object array
    object[,] excelArr = new object[maxRowCount, maxColCount];

    //Dequeue data from Queue and populate object matrix
    int i = 0;
    while(dataQueue.Count > 0){
        string[] eData = dataQueue.Dequeue();

        for(int j = 0; j < eData.Length; j++){
            excelArr[i, j] = eData[j];
        }

        i++;
    }

    //Write data to range
    Excel.Range c1 = (Excel.Range)wsh.Cells[startRow, startCol];
    Excel.Range c2 = (Excel.Range)wsh.Cells[startRow + maxRowCount - 1, maxColCount];
    Excel.Range range = worksheet.Range[c1, c2];

    range.Value2 = excelArr;

    //Tried not to touch this stuff
    ws.Cells[ws.Dimension.Address].AutoFitColumns();

    Byte[] bin = p.GetAsByteArray();
    using(FileStream fs = File.OpenWrite("C:\\test.xlsx")) {
        fs.Write(bin, 0, bin.Length);
    }
}

我没有尝试编译此代码,因此请仔细检查所使用的索引;并检查是否有任何小的语法错误。

要考虑性能的一些额外指示:

  • 尝试parallel对象数组的填充,因为它主要是基于索引的(可能有一个带有索引跟踪器Dictionary<int, string[]>的字典)并在那里查找以便更快地填充对象数组。你可能不得不换空间。
  • 查看您是否能够对列和行计数进行硬编码,或快速计算出来。在我的代码修复中,我设置了计数器来动态计算最大行数和列数;我不建议将其作为永久解决方案。
  • AutoFitColumns成本非常高,特别是如果您要处理超过一百万行