Laden...

DataTable unglaublich langsam

Erstellt von Sythus vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.710 Views
S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 9 Jahren
DataTable unglaublich langsam

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:

  • Die Datatable wurde aus einem Excel File erstellt, ist zu diesem Zeitpunkt aber schon komplett vorhanden.
  • Das umwandeln der Datatable in IEnumerable ist hier unbeachtet (das würde ich noch ändern)
  • Es geht hier nur darum die spalten in einzelne Listen zu schreiben. Warum dauert das so lange?
  • Es handelt sich um eine MVC 4 Anwendung
  • Es läuft in einem Thread

Ich bin für jeden Hinweis überaus dankbar.

Liebe Grüße,
Sythus

16.834 Beiträge seit 2008
vor 9 Jahren

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?

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 9 Jahren

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.

16.834 Beiträge seit 2008
vor 9 Jahren

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.

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 9 Jahren

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 😃

16.834 Beiträge seit 2008
vor 9 Jahren

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.

Gelöschter Account
vor 9 Jahren

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.

3.170 Beiträge seit 2006
vor 9 Jahren

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

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 9 Jahren

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

771 Beiträge seit 2009
vor 9 Jahren

herbivore hätte wohl jetzt den Link auf Schlemiel gebracht wie in Datenbank-Importvorgang wird langsamer 😉

S
Sythus Themenstarter:in
166 Beiträge seit 2009
vor 9 Jahren

Danke für den Link. Wirklich sehr interessant was Herbivore da schreibt.