I believe that the reason for the results you are seeing is the fact that on the first run the .NET CLR must JIT the code. Since SpreadsheetGear is a complete spreadsheet engine under the hood (as opposed to a read / write library) there is more code to JIT - thus the first run is taking longer for SpreadsheetGear than EPPlus (I am speculating here but have a great deal of experience in benchmarking .NET code over the last 10 years).
I do not have EPPlus installed but I did write a test which tries to do the same thing you are doing. with SpreadsheetGear 2012 Since I don't have your starting workbook I first build the workbook. Then, I used more optimal SpreadsheetGear APIs. The first time I run I get 141 milliseconds for SpreadsheetGear 2012. After the first run I get 9 or 10 milliseconds for each run on an overclocked Core i7-980x running Win7 x86 and a release build run without debugger.
I have pasted my code below (just paste it into a .NET 4.0 C# console application).
One more thought I have is that this is a very small test case. To really see the performance of SpreadsheetGear 2012 try this with 100,000 rows or even 1 million rows.
Disclaimer: I own SpreadsheetGear LLC
using System;
using System.Collections.Generic;
using System.Diagnostics;
using SpreadsheetGear;
namespace SGvsEPPlus
{
class Program
{
internal struct Item
{
internal Item(int id, string title, int value)
{
Id = id;
Title = title;
Value = value;
}
internal int Id;
internal string Title;
internal int Value;
}
static void Test(int rows)
{
string filename = @"C:\tmp\MyWorkbook.xlsx";
Console.Write("Test({0})...", rows);
var timer = new Stopwatch();
// Create workbook since we don't have poster's original workbook.
timer.Restart();
var workbook = Factory.GetWorkbook();
var values = (SpreadsheetGear.Advanced.Cells.IValues)workbook.Worksheets[0];
for (int row = 1; row <= rows; row++)
{
values.SetNumber(row, 0, row);
values.SetText(row, 1, "Title " + row);
values.SetNumber(row, 2, row * 10);
}
Console.Write("Create workbook={0:0}...", timer.Elapsed.TotalMilliseconds);
// Save workbook
timer.Restart();
workbook.SaveAs(filename, FileFormat.OpenXMLWorkbook);
Console.Write("Save workbook={0:0}...", timer.Elapsed.TotalMilliseconds);
// Track total time of original test.
var totalTimer = Stopwatch.StartNew();
// Open workbook
timer.Restart();
var excelIn = Factory.GetWorkbook(filename);
Console.Write("Open excelIn={0:0}...", timer.Elapsed.TotalMilliseconds);
// Copy workbook to list
timer.Restart();
var sheetIn = excelIn.Worksheets[0];
var valuesIn = (SpreadsheetGear.Advanced.Cells.IValues)sheetIn;
var rowIn = 1;
var data = new List<Item>(rows);
while (valuesIn[rowIn, 0] != null)
{
data.Add(new Item(
(int)valuesIn[rowIn, 0].Number,
valuesIn[rowIn, 1].Text,
(int)valuesIn[rowIn, 2].Number));
rowIn++;
}
excelIn.Close(); // Not necessary but left for consistency.
Console.Write("excelIn->data={0:0}...", timer.Elapsed.TotalMilliseconds);
timer.Restart();
var excelOut = Factory.GetWorkbook();
var sheetOut = excelOut.Worksheets[0];
var valuesOut = (SpreadsheetGear.Advanced.Cells.IValues)sheetOut;
sheetOut.Name = "Out";
var rowOut = 0;
sheetOut.Cells[rowOut, 0, rowOut, 2].Font.Bold = true;
sheetOut.Cells[rowOut, 0].Value = "Id";
sheetOut.Cells[rowOut, 1].Value = "Title";
sheetOut.Cells[rowOut++, 2].Value = "Value";
foreach (var item in data)
{
valuesOut.SetNumber(rowOut, 0, item.Id);
valuesOut.SetText(rowOut, 1, item.Title);
valuesOut.SetNumber(rowOut, 2, item.Value);
rowOut++;
}
Console.Write("data->excelOut={0:0}...", timer.Elapsed.TotalMilliseconds);
timer.Restart();
excelOut.SaveAs(@"C:\tmp\SpreadsheetGear_" + Guid.NewGuid() + ".xlsx", FileFormat.OpenXMLWorkbook);
excelOut.Close(); // Again - not necessary.
Console.WriteLine("Save excelOut={0:0}...", timer.Elapsed.TotalMilliseconds);
Console.WriteLine(" Total={0:0}", totalTimer.Elapsed.TotalMilliseconds);
}
static void Main(string[] args)
{
// Do it three times with 1000 rows. Note that the first
// time takes longer because code must be JITted.
Test(1000);
Test(1000);
Test(1000);
}
}
}