So Leute, ich mal wieder :D
Heute geht es um etwas echt "heikles" und ich bin völlig ratlos, wieso das "so lange" dauert.
Ich habe drei (beteiligte) Klassen, die wie folgt zusammenhängen:
1. Die Klasse AalVariable speichert einen Wert (oder mehrere Werte je nach "Benutzung"). Das heißt ein Objekt der Klasse AalVariable speichert einen Wert in einem std::vector<void*>. Wobei es zusätzlich ein zweites Array gibt, welches den Typ des abgespeicherten Wertes (string/long long/double) enthält, damit der Wert später auch richtig gecastet werden kann.
2. Die Klasse AllocationHelper ist eine Klasse, die dabei hilft allozierten Speicher wiederzuverwenden (damit spare ich mir einige Tausend news/deletes).
3. Die Klasse Interpreter bekommt eine Reihe Befehle "zu geschmissen" und interpretiert diese. Momentan "nur" Die Grundrechenarten, das Abspeichern der Werte in Variablen (Dafür benutze ich Objekte der Klasse AalVariable) und Schleifen.
Beispielsweise diesen Pseudocode/AutoIt:
Braucht meine Klasse von Interpreter rund 700ms.
Der AutoIt-Interpreter braucht 900ms.
Soweit so gut, aber nachdem mir diese 700ms noch viel zu lange vorkamen, habe ich einmal die einzelnen Code-Stücke gebenchmarkt und dabei ist mir etwas sehr komisches aufgefallen:
Das Parsen an sich dauert (für den kompletten Durchlauf) um die 50ms, also vollkommen ok.
Die Berechnungen/das Abspeichern selbst dauert auch nur etwas um die 50ms.
Das hat mich enorm stutzig gemacht also habe ich noch mehr gebenchmarkt und herausgefunden, dass der Flaschenhals (scheinbar?) bei meinem Hauptspeicher bzw. genauer gesagt beim Übertragungsweg liegt.
Dieses Stück Code (für alle Aufrufe also einen kompletten Durchlauf, ziemlich genau 3.000.000 Aufrufe) braucht um die 500ms:
mit diesem Code-Stück hole ich mir die Variable (alle Variablen sind mit long long durchnummeriert), sollte sie bereits deklariert worden sein aus "std::map<long long,AalVariable*> globalVars;", ansonsten hole ich mir über den AllocationHelper eine neue Instanz der Variable.
Wie erwartet funktioniert der Interpreter richtig und holt sich lediglich einmal (vor der While-Schleife) eine neue Instanz. Das heißt der Else-Zweig wird genau einmal ausgeführt.
Damit ist der Flaschenhals also dieses Stück Code:
Am Anfang dachte ich es liegt an meiner map, dass der Zugriff so enorm lange braucht, also habe ich den Code dann testweise in einen Vektor umgeschrieben, also in etwa das:
Aber auch das hat mir "nur" 10ms eingespart (was auch zu erwarten war, weil die map ja nur aus 2 Variable besteht). Das heißt statt 500ms nimmt die Funktion nur noch 490ms in Anspruch, was mir immer noch viel zu viel ist.
Ohne dass ich es mit meinen eigenen Augen gesehen/ausprobiert hätte, würde ich sagen, dass realistsiche Werte irgendwo bei 50-100ms liegen sollten.
Aber egal was ich mache ich komme einfach nicht auf weniger.
Mein Code besitzt keinerlei Multithreading/Windows spezifischen Code.
Sowohl auf Linux als auch auf Windows ist die Funktion (bzw. das if) der Flaschenhals (bisher auf 3 Pcs getestet, 2x Windows, 1x Linux/Ubuntu).
Meine Header-Dateien/Klassen (unwichtige Methoden habe ich rausgenommen) sehen dabei wie folgt aus:
Interpreter.h
Aalvariable.h
AllocationHelper.h
Habt ihr eine Idee wieso der Flaschenhals ausgerechnet dort entsteht und sich auch nicht durch Vektoren beheben lässt? Gibt es vielleicht zu viele Cache-Misses? Kann ich mir aber auch nicht vorstellen, weil der benutze Speicher wohl sogar locker in ein Level 1/2 Cache passt (sind ja nur paar Zeilen).
Aber noch wichtiger als die Frage nach dem warum ist mir vor allem, wie behebe ich das bzw. wie mache ich den Code schneller/löse den Flaschenhals auf?
Edit:
Laut Taskmanager braucht mein Prozess 650K, sollte also sogar noch in einen Level 2/3 Cache reinpassen, theoretisch, wäre natürlich doof wenn genau immer dieser Bereich überschrieben werden würde.
Heute geht es um etwas echt "heikles" und ich bin völlig ratlos, wieso das "so lange" dauert.
Ich habe drei (beteiligte) Klassen, die wie folgt zusammenhängen:
1. Die Klasse AalVariable speichert einen Wert (oder mehrere Werte je nach "Benutzung"). Das heißt ein Objekt der Klasse AalVariable speichert einen Wert in einem std::vector<void*>. Wobei es zusätzlich ein zweites Array gibt, welches den Typ des abgespeicherten Wertes (string/long long/double) enthält, damit der Wert später auch richtig gecastet werden kann.
2. Die Klasse AllocationHelper ist eine Klasse, die dabei hilft allozierten Speicher wiederzuverwenden (damit spare ich mir einige Tausend news/deletes).
3. Die Klasse Interpreter bekommt eine Reihe Befehle "zu geschmissen" und interpretiert diese. Momentan "nur" Die Grundrechenarten, das Abspeichern der Werte in Variablen (Dafür benutze ich Objekte der Klasse AalVariable) und Schleifen.
Beispielsweise diesen Pseudocode/AutoIt:
PHP Code:
$var=0
$var2=0
while $var2<1000000
$var+=1
$var2+=1
WEnd
Der AutoIt-Interpreter braucht 900ms.
Soweit so gut, aber nachdem mir diese 700ms noch viel zu lange vorkamen, habe ich einmal die einzelnen Code-Stücke gebenchmarkt und dabei ist mir etwas sehr komisches aufgefallen:
Das Parsen an sich dauert (für den kompletten Durchlauf) um die 50ms, also vollkommen ok.
Die Berechnungen/das Abspeichern selbst dauert auch nur etwas um die 50ms.
Das hat mich enorm stutzig gemacht also habe ich noch mehr gebenchmarkt und herausgefunden, dass der Flaschenhals (scheinbar?) bei meinem Hauptspeicher bzw. genauer gesagt beim Übertragungsweg liegt.
Dieses Stück Code (für alle Aufrufe also einen kompletten Durchlauf, ziemlich genau 3.000.000 Aufrufe) braucht um die 500ms:
PHP Code:
inline AalVariable* Interpreter::getAalVar(const long long tmp)
{
if(currHierarchyLevel==0)
{
auto it=globalVars.find(tmp);
if(it!=globalVars.end())
{
return it->second;
}
else
{
AalVariable* tmpVar=allocator.allocateAalVar();
globalVars[tmp]=tmpVar;
return tmpVar;
}
}
else
{
//todo: implement later
return nullptr;
}
}
Wie erwartet funktioniert der Interpreter richtig und holt sich lediglich einmal (vor der While-Schleife) eine neue Instanz. Das heißt der Else-Zweig wird genau einmal ausgeführt.
Damit ist der Flaschenhals also dieses Stück Code:
PHP Code:
auto it=globalVars.find(tmp);
if(it!=globalVars.end())
{
return it->second;
}
PHP Code:
if(globalVars[tmp]!=nullptr)
{
return globalVars[tmp];
}
Ohne dass ich es mit meinen eigenen Augen gesehen/ausprobiert hätte, würde ich sagen, dass realistsiche Werte irgendwo bei 50-100ms liegen sollten.
Aber egal was ich mache ich komme einfach nicht auf weniger.
Mein Code besitzt keinerlei Multithreading/Windows spezifischen Code.
Sowohl auf Linux als auch auf Windows ist die Funktion (bzw. das if) der Flaschenhals (bisher auf 3 Pcs getestet, 2x Windows, 1x Linux/Ubuntu).
Meine Header-Dateien/Klassen (unwichtige Methoden habe ich rausgenommen) sehen dabei wie folgt aus:
Interpreter.h
Aalvariable.h
AllocationHelper.h
Habt ihr eine Idee wieso der Flaschenhals ausgerechnet dort entsteht und sich auch nicht durch Vektoren beheben lässt? Gibt es vielleicht zu viele Cache-Misses? Kann ich mir aber auch nicht vorstellen, weil der benutze Speicher wohl sogar locker in ein Level 1/2 Cache passt (sind ja nur paar Zeilen).
Aber noch wichtiger als die Frage nach dem warum ist mir vor allem, wie behebe ich das bzw. wie mache ich den Code schneller/löse den Flaschenhals auf?
Edit:
Laut Taskmanager braucht mein Prozess 650K, sollte also sogar noch in einen Level 2/3 Cache reinpassen, theoretisch, wäre natürlich doof wenn genau immer dieser Bereich überschrieben werden würde.