Laden...

Performance einsparen bei Methodenaufruf (2,8 Billiarden Aufrufe / Methodenrumpf ist optimiert)

Erstellt von falsecode vor 9 Jahren Letzter Beitrag vor 9 Jahren 4.138 Views
F
falsecode Themenstarter:in
55 Beiträge seit 2013
vor 9 Jahren
Performance einsparen bei Methodenaufruf (2,8 Billiarden Aufrufe / Methodenrumpf ist optimiert)

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

1.361 Beiträge seit 2007
vor 9 Jahren

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

W
872 Beiträge seit 2005
vor 9 Jahren

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.

F
falsecode Themenstarter:in
55 Beiträge seit 2013
vor 9 Jahren

@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.

Hinweis von herbivore vor 9 Jahren

Unbekannte Begriffe bitte immer selbst nachschlagen:

Loop unrolling
Inline-Ersetzung

P
40 Beiträge seit 2011
vor 9 Jahren

@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?

F
falsecode Themenstarter:in
55 Beiträge seit 2013
vor 9 Jahren

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.

T
1 Beiträge seit 2014
vor 9 Jahren

@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.

M
334 Beiträge seit 2007
vor 9 Jahren

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".

16.835 Beiträge seit 2008
vor 9 Jahren

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.

3.825 Beiträge seit 2006
vor 9 Jahren

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

1.361 Beiträge seit 2007
vor 9 Jahren

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

3.825 Beiträge seit 2006
vor 9 Jahren

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

2.891 Beiträge seit 2004
vor 9 Jahren

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;
	}
}