003
15.04.2009, 20:59 Uhr
0xdeadbeef
Gott (Operator)
|
Der generelle Konsensus ist, dass Optimierung erst am Ende kommt, das ist insbesondere für Mikrooptimierungen der Fall. Wenn du anfängst, ein Programm zu schreiben, ist erstmal wichtig, dass es tut, was es soll, und dass es dabei stabil läuft. Es ist dabei nützlich, auf das Laufzeitverhalten zu kucken, also wie der Code für größere Datenmengen skaliert, aber Mikrooptimierungen kommen erst ganz am Ende - dann nämlich, wenn du weißt, wo die Flaschenhälse auftauchen, die es zu entfernen gilt.
Prinzipiell empfiehlt es sich deswegen, mit einfach zu benutzenden und stabilen Konstrukten anzufangen, wie etwa den Containern der Standardbibliothek oder Boost-Bibliotheken. Man kommt schneller ans vorläufige Ziel, und man hat verlässlichen Code, der dir, sobald es an die Optimierung geht, ein ausreichend stabiles Umfeld gibt, diese in Ruhe umzusetzen - man muss sich nicht um Wechselwirkungen mit anderen, instabilen Codeteilen sorgen.
Auch ist es meiner Erfahrung nach oft so, dass Geschwindigkeitsprobleme gerade mit der Standardbibliothek aus einer suboptimalen Verwendung derselbigen entstehen. Zum Beispiel ist
C++: |
std::vector<int> v;
for(int i = 0; i < 1000; ++i) { v.push_back(i); }
|
auf etwas versteckte Weise sehr langsam, weil der Vektor dauernd erweitert werden muss - dies stellt man dann beispielsweise in der Optimierungsphase per Profiler fest, schlägt sich an die Stirn und schreibt zunächst
C++: |
std::vector<int> v;
v.reserve(1000);
for(int i = 0; i < 1000; ++i) { v.push_back(i); }
|
...und wenn die Anzahl Elemente wirklich von Vorneherein genau bekannt ist, fasst man sich dann erneut an die Stirn und schreibt
C++: |
std::vector<int> v(1000);
for(int i = 0; i < 1000; ++i) { v[i] = i; }
|
...wobei letzteres einen geringeren Geschwindigkeitsgewinn mit sich bringt als die erste Optimierung, es geht da vor allem darum, dass v.size() nicht dauernd angepasst werden muss. Wir hatten vor einer Weile einen selbsternannten "Optimierungsprofi" im Forum, der etwas mit dem ersten Codestück vergleichbares mit der Hashtable-Implementierung der SGI-STL abgezogen und daraus hergeleitet hatte, dass ein flaches 512MB-Array ein schnellerer Ansatz zur Zahlenspeicherung sei.
Wie dem auch sei, generische Bibliotheken bringen einen gewissen Overhead mit sich, allerdings ist der in der Regel nicht von großer Bedeutung und/oder vermeidbar, wenn man sich Gedanken darum macht, was im Hintergrund eigentlich passiert. Dieser geringe Nachteil wird durch die deutlich niedrigere Entwicklungs- und Debugzeit in aller Regel mehr als wieder wettgemacht, und wenn das Programm erstmal steht, kann man sich sehr viel leichter Gedanken darum machen, ob man statt std::vector<std::string> lieber boost::ptr_vector<std::string> oder char*[] benutzen will. -- Einfachheit ist Voraussetzung für Zuverlässigkeit. -- Edsger Wybe Dijkstra |