Hallo zusammen,
ich hoffe ich habe den richtigen Bereich für meine Frage gefunden.
Ich habe eine DataTable (workSheet) die ich wie folgt zu IEnumerable<DataRow> umwandle:
IEnumerable<DataRow> rows = from DataRow row in workSheet.Rows
select row;
Durch diese Zeilen iteriere ich dann und hole mir aus jeder Zeile die Werte die ich benötigte und schreibe diese in ein Dictionary.
for (int i = 1; i < rows.Count(); i++)
{
DataRow row = rows.ElementAt(i);
for (int j = 0; j < rules.Count; ++j)
{
dataDict[rules[j].headerName].Add(row.ItemArray[j].ToString());
}
}
Die Datatable hat ca. 100 000 Zeilen. Von jeder Zeile werden je 7 Werte in unterschiedliche Dictionary's geschrieben. (Pro Spalte 1 Dictionary)
D.h. das diese Schleife ca. 700 000 mal durchlaufen wird.
Wenn ich diese laufen lasse und nach 10 Minuten einen Breakpoint setze ist die Schleife gerade mal bei Datensatz 5000.
Ich habe etwas gegoogelt und ganz andere zeitliche Zahlen gefunden.
Was läuft da falsch? Was ist so unglaublich unperformant?
Ein paar Zusatzinformation:
Ich bin für jeden Hinweis überaus dankbar.
Liebe Grüße,
Sythus
Was ist denn workSheet für ein Typ?
Mal mit einem .NET PRofiler den Code analyisiert, was der Hotspot ist?
Wo ist der Sinn, dass Du 100.000 Zeilen auf einmal liest?
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Sorry die wichtige Information habe ich vergessen.
Worksheet ist ein DataTable
Ich muss die Dateien lesen, validieren und dann in eine SQL Datenbank packen. D.h. ich muss diese Dateien Menge anDaten lesen (was letztendlich auch nur 45 MB sind)
Mit dem Profiler versuche ich mal. Aber die Problematik dreht sich wirklich nur um diese Forschleife.
Danke für dein Feedback.
Ist Dir klar, dass
IEnumerable<DataRow> rows = from DataRow row in workSheet.Rows
select row;
das gleiche wie
IEnumerable<DataRow> rows = workSheet.Rows;
ist?
Ich weiß nicht, wie Du das Excel liest ( das geht ja auch nicht mit Zauberei ) aber
IList<DataRow> rows = workSheet.Rows.ToList();
vermeidet schon mal, dass Du städnig den Enumerator durchläufst.
Den restlichen Heat erkennst Du unter Garantie mit einem Profiler.
Dass es nur die for-Schleife ist bezweifle ich sehr stark.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo Abt,
vielen Dank dafür. Das war mir nicht klar. Das probiere ich auf jeden Fall aus.
Aber eine Frage dazu (Interesse halber):
Spielt es noch eine Rolle wie ich die Excel Datei lese wenn ich sie bereits in meiner DataTable habe? Kann die Art und Weise irgendwie einen Einfluss darauf haben, wenn ich da durch iteriere?
Ich habe bisher noch mit keinem Profiler gearbeitet. Habe Visual Studio 2013. Ich versuche mal, ob ich das irgendwie hinbekomme. Gibts da einen bestimmten den ich nutzten sollte?
An den Dictionaries kann es nicht liegen oder?
Tausend Dank 😃
In einer der ersten Suchtreffer, wenn Du in diesem Forum nach 'Profiler' suchst, findest Du einen Beitrag von mir, der auf Einführung in die Leistungsprofilerstellung verweist.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ich will mich in die eigentliche Debatte eigentlich garnicht einmischen aber mir ist aufgefallen das du scheinbar Linq Queries über ein Excel WorkSheet anwendest und damit auch implizit irgendwie COM Interop nutzt( es sei denn du verwendest spezielle Open-XML Wrapper). Für mich erklärt sich der Performance Drop dadurch das du Linq über COM anwendest und das funktioniert ungepatcht einfach nicht bei einer Grössenordnung von 100.000. COM Aufrufe sind dafür viel zu teuer aus der Performance Sicht.
Excel bietet viele Funktionen an um Zellen nach Kriterien zu filtern.
Anders gesagt: Jeder Aufruf zu Excel via Interop aus .NET kostet nur für die Verwaltung wenigstens 100 Millisekunden. Excel selbst kann kann interne Berechnungen für Millionen Zellen unter 1 viertel Millisekunde durchführen.
So oder so die DataTable ist für dein Performance Problem nicht verantwortlich, so glaube ich. Reduziere deine Excel Aufrufe. (Du kannst ja mal hochrechnen was eine Linq Query auf 100.000 Zellen Einzelaufruf mal 100-500 millisekunden-Verwaltungsaufwand pro Abruf der der Methode oder Property) so alles anrichtet. Excel bietet Methoden an die das intern mit nur einem Aufruf lösen und das in ein paar Nanosekunden.
Wenn du mit Excel dealst dann musst du verstehen das Excel zu COM gehört und das funktioniert - in sich selbst - anders(viel effiezienter) als .NET. Aufgrund deines MVC4 Hinweisesgehe ich davon aus das du hier vermutlich aus einer Server Anwendung agierst die Reports generiert. Dazu bräuchtest du ab Excel 2007 keine Office Automatisierung sondern nur ein paar XML Kentnisse, da ab hier das Dateiformat offen ist.
Hallo,
1.
for (int i = 1; i < rows.Count(); i++)
Bei diesem Schleifenkopf wird bei jedem Schleifendurchlauf das komplette IEnumerable enumeriert um es zu zählen!
2.
DataRow row = rows.ElementAt(i)
Damit wird wieder jeweils das ganze Enumerable von vorne enumeriert, jeweils bis zum Element i.
Wenn Du das änderst, klappts bestimmt auch besser mit der Performance.
Nimm z.B. einfach
foreach (DataRow row in rows)
Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
Herzlichen Dank an alle.
Die Ausführungszeit konnte mit euren Tips auf 10 Sekunden reduziert werden.
Maßgebend dafür waren tatsächlich Count, ElementAt und Linq.
Die ExcelDatei wird mit IExcelDataReader einmalig gelesen und in eine DataTable umgewandelt.
Das dauert initial 35-40 Sekunden, wäre schneller natürlich schöner, aber die restliche Datenbearbeitung geht dann sehr schnell.
Viele Grüße,
Sythus
herbivore hätte wohl jetzt den Link auf Schlemiel gebracht wie in Datenbank-Importvorgang wird langsamer 😉