Hallo Community,
ich habe ein Programm geschrieben welches Daten aus einer Datenbank analysiert. Es handelt sich dabei um 2,8 Billionen Datensätze 😁 .
Jeder Datensatz durchläuft jetzt eine Reihe von Methoden, dabei handelt es sich oft um die gleichen Methoden. Meine Frage ist jetzt wo man ganz generelle Optimierungen vornehmen kann. Da es in meinem Fall länger dauert die Methoden aufzurufen als sie auszuführen, versuche ich beim Aufruf performance zu gewinnen, da die Methoden ansich schon sehr nah ans Limit optimiert sind.
Ich dachte da an Dinge wie alle Methoden in eine große zu stopfen, das ganze in so etwas wie ein Buffer zu laden usw. 🤔
Habe in dieser Richtung keinerlei Erfahrungen und hoffe das Ihr mir weiterhelfen könnt.
lg falsecode
Hi falsecode,
Ich bezweifle, dass es sich um 2,8 Billionen (2800 Milliarden oder auch 2,8 * 10^12) Datensätze handelt 😉 Ein Bit pro Datum macht dann schon 350 GB. Das schiebt man nicht einfach so zwischen Datenbank und Programm hin und her 😉
Meinst du vielleicht Milliarden?
Kannst du die Logik direkt in SQL abbilden?
Zwar kann der compiler schon viel optimieren, aber achte darauf, dass du im Release baust, dass alles gewährleistet ist, dass der Compiler Schleifen entrollen und Methoden inlinen kann. Oder du machst das eben "von Hand".
Parallelisierung ist auch ein guter Weg
Vektorisierung (SSE-Instruktionen) ebenfalls, dann müsstest du aber wahrscheinlich auf C/C++ ausweichen.
Was machst du denn genau mit den Daten?
beste Grüße
zommi
Performance optimieren bedeutet erstmal, daß Du misst, was wieviel Zeit braucht und Du Flaschenhälse/Engpässe findest (Memory/CPU/IO).
Bei Deinem Problem ist die Frage vor allem, ob Du die Aufrufe parallelisieren kannst und wie schnell Du die Daten aus der DB bekommst.
@zommi
Ich weiß wie viel 2,8 Billionen sind, es handelt sich wirklich um Billionen 😉
Nein ich kann das ganze nicht in SQL abbilden. In eine anderen Programmiersprache das ganze umzusetzen kommt nicht Infrage, dazu ist das Projekt wirklich zu groß.
Was genau mit "Schleifen entrollen und Methoden inlinen" gemeint ist kann ich nicht wirklich nachvollziehen. Das ganze zu parallelisieren stand schon auf meiner ToDo-Liste, wollte das jedoch erst machen wenn ich bereits alles andere ausgereitzt habe.
Nun ich lese die Daten wobei es sich um Daten vom Typ nchar handelt blockweise ein und schicke sie an meine Methoden. Hier wird jeder einzelne Datensatz der nun weiter als string behandelt wird auf seinen Inhalt und die Reihenfolge seines Inhalt überprüft.
Die Struktur sieht sinngemäß so aus:
Methode 1();
Methode 2();
Wenn Methode 1() und Methode 2() gleich true
Methode 3()
if Methode3() gleich true -> Abbruch
else Methode4() if Methode4() gleich true -> Abbruch
else Methode5() if Methode5() gleich true -> Abbruch
else Methode6() if Methode6() gleich true -> Abbruch
else [...]
@weismat
Ich habe bereits alles durchgemessen, daher weiß ich das der Aufruf meiner Methoden der Punkt ist, der am meisten Zeit in Anspruch nimmt und das dürfte ja wohl nicht sein.
Unbekannte Begriffe bitte immer selbst nachschlagen:
@weismat
Ich habe bereits alles durchgemessen, daher weiß ich das der Aufruf meiner Methoden der Punkt ist, der am meisten Zeit in Anspruch nimmt und das dürfte ja wohl nicht sein.
Also der Aufruf einer Methode sollte eig keine Zeit kosten, sondern nur das "Abarbeiten" der Methode.
Wie hast du denn gemessen?
Ich habe wie folgt gemessen, einmal das ganze mit Aufruf einer Methode und einmal direkt.
Direkt:
Stopwatch sw = Stopwatch.StartNew();
do_something;
sw.stop
Mit Methodenaufruf:
Stopwatch sw = Stopwatch.StartNew();
Methode();
sw.stop
private void Methode()
{
do_something;
}
Das ganze dann ein paar tausend mal und dann den Durschnitt genommen.
@weismat
Ich habe bereits alles durchgemessen, daher weiß ich das der Aufruf meiner Methoden der Punkt ist, der am meisten Zeit in Anspruch nimmt und das dürfte ja wohl nicht sein.
Wenn der Aufruf deiner Methoden direkt das Problem ist, kannst du nicht versuchen deine Methoden 1,2,3,... in einer einzigen Methode unterzubringen? Das würde es zwar sicher etwas komplexer machen, aber dafür würdest du pro Datensatz nur eine Methode aufrufen.
Ich habe wie folgt gemessen, einmal das ganze mit Aufruf einer Methode und einmal direkt.
Debug- oder Release-Build? Hast du das ganze auch mehrmals gemacht?
Ich kann dir nur dazu raten, was zommi und weismat auch schon gesagt haben: Um ein Problem zu beheben muss man erst mal das Problem erkennen.
Ungezielt drauf los zu basteln ist immer eine schlechte Idee, denn wir alle wissen -> "premature optimization is the root of all evil".
Du wirst 2,8 Billionen Objekte kaum im Speicher halten. Wieso wurde schon genannt.
Wie holst Du also die Elemente aus der Datenbank, Schubweise?
Micro-Optimierungen sind mit Vorsicht zu genießen und Micro-Performance-Measurement wie Du es hier mit StopWatch auf Methoden-Köpfe machts schon gar 3 mal.
Verwende einen Profiler, um zu Erkennen, wo genau der Flaschenhals ist - und nicht Stopwatch.....
Anschließend kannst Du Dich auf sowas wie AggressiveInlining, Pipelining und Co stürzen.
Aber so geht der Schuss sicher eher nach Hinten los.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Hallo falsecode,
ich hab mal das Lesen von Datensätzen per ADO.NET DataReader getestet, die schnellste Art aus einer Datenbank zu lesen.
Kleine Tabelle, SQL Server 2012, Datenbank liegt auf SSD-Array. Schneller bekomme ich das hier nicht hin.
Ergebnis : 154.942 Datensätze in 17 Sekunden.
Hochgerechnet auf 2,8 Billionen : 3589 Tage 18 Stunden 51 Minuten 31 Sekunden.
Das sind fast 10 Jahre !!!
Ich glaube ein Methodenaufruf ist da dein kleinstes Problem.
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Nein ich kann das ganze nicht in SQL abbilden.
Vielleicht geht es ja trotzdem. Filtern auf Basis von ein paar String-Eigenschaften klingt nicht so komplex.
Woran scheitert die Umsetzung mittels SQL? Ich würde das als allererstes probieren. Viele Datenbanksysteme arbeiten von hause aus hoch-optimiert und parallel.
In eine anderen Programmiersprache das ganze umzusetzen kommt nicht Infrage, dazu ist das Projekt wirklich zu groß.
Du musst ja nicht das ganze Projekt portieren. Es gibt ja mit PlatformInvoke/Interop/ManagedC++/COM/SharedMemory/Pipes/Sockets... unzählige Möglichkeiten wie Programmteile unterschiedlicher Sprachen miteinander kommunizieren können.
Du musst nur den performance-kritischen Teil portieren.
Und entweder ist der klein genug und die Logik einfach genug, dass das kein Problem ist das in einer anderen Sprache nachzubauen.
Oder der Code ist so umfangreich, dass du dir selbst widerspricht: Denn wenn es komplexe Berechnungen sind, dann kann der Methodenaufruf nicht der Flaschenhals sein 😉
beste Grüße
zommi
Schau Dir mal :
Microsoft StreamInsight - Technet
Verarbeiten großer verteilter Datenmengen mit Hadoop | heise Developer
Grüße Bernd
Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3
Wenn es ein MS SQL Server ist: Man kann auch CLR-Sachen direkt in den SQL Server laden. Siehe z.B. Erstellen von Datenbankobjekten mit CLR-Integration (Common Language Runtime). Die Logik kannst du dann "ganz normal" mit .NET/C#-Mitteln implementieren und die markierten Methoden dann direkt in einem SQL-Statement aufrufen.
Das spart dann das Abfragen und Übertragen von Daten aus dem SQL-Server in die .NET-Anwendung (und ggf. wieder zurück). Zudem kann der SQL-Server noch diverse Optimierungen anwenden.
[SqlFunction(IsDeterministic=true,IsPrecise=true)]
public static int EditDistance(string first,string second,int limit)
{
...
}
[SqlFunction(IsDeterministic=true)]
public static string Normalize(string value)
{
if (String.IsNullOrEmpty(value))
return value;
else
{
// alles klein schreiben
string result = value.ToLower();
result = Regex.Replace(result,@"\s+"," ");
// verschiedene Arten "straße"/"strasse"/"str" vereinheitlichen
result = Regex.Replace(result,"stra(ß|ss)e","str");
// verschiedene Artten deutscher Sonderzeichen vereinheitlichen
result = Regex.Replace(result,"ä|ae","a");
result = Regex.Replace(result,"ö|oe","o");
result = Regex.Replace(result,"ü|ue","u");
result = Regex.Replace(result,"é|è","e");
result = Regex.Replace(result,"ß","ss");
return result;
}
}