performance - waisenstrasse - zur letzten instanz köln




Leistungsoptimierungsstrategien der letzten Instanz (20)

Da es sich bei vielen der Leistungsprobleme um Datenbankprobleme handelt, gebe ich Ihnen einige spezifische Dinge, die Sie beim Optimieren von Abfragen und gespeicherten Prozeduren beachten sollten.

Vermeiden Sie Cursor in den meisten Datenbanken. Vermeiden Sie auch Schleifen. In den meisten Fällen sollte der Datenzugriff auf den Datensätzen basieren, nicht auf der Datensatzverarbeitung. Dies beinhaltet nicht die Wiederverwendung einer einzelnen gespeicherten Datensatzprozedur, wenn Sie 1.000.000 Datensätze gleichzeitig einfügen möchten.

Verwenden Sie niemals Select *, geben Sie nur die Felder zurück, die Sie tatsächlich benötigen. Dies trifft insbesondere dann zu, wenn es Joins gibt, da die Join-Felder wiederholt werden und somit unnötige Belastung sowohl für den Server als auch für das Netzwerk verursachen.

Vermeiden Sie die Verwendung korrelierter Unterabfragen. Verwenden Sie Joins (einschließlich Joins zu abgeleiteten Tabellen, wo möglich) (Ich weiß, das gilt für Microsoft SQL Server, aber testen Sie den Rat, wenn Sie ein anderes Backend verwenden).

Index, Index, Index. Und holen Sie sich diese Statistiken, falls dies für Ihre Datenbank zutrifft.

sargable Sie die Abfrage als sargable . Das bedeutet, dass Dinge vermieden werden, die es unmöglich machen, die Indizes zu verwenden, z. B. die Verwendung eines Platzhalters im ersten Zeichen einer Like-Klausel oder einer Funktion im Join oder als linken Teil einer Where-Anweisung.

Verwenden Sie korrekte Datentypen. Es ist schneller, Datumsberechnungen für ein Datumsfeld durchzuführen, als zu versuchen, einen Zeichenfolgendatentyp in einen Datumsdatentyp zu konvertieren, und dann die Berechnung durchzuführen.

Stecken Sie niemals eine Schlaufe in einen Auslöser!

Die meisten Datenbanken können überprüfen, wie die Abfrage ausgeführt wird. In Microsoft SQL Server wird dies als Ausführungsplan bezeichnet. Überprüfen Sie zuerst, wo Problembereiche liegen.

Berücksichtigen Sie, wie oft die Abfrage ausgeführt wird und wie lange es dauert, bis ermittelt wird, was optimiert werden muss. Manchmal können Sie durch eine geringfügige Anpassung an eine Abfrage, die millionenfach am Tag ausgeführt wird, mehr Leistung erzielen als durch das Löschen einer long_running-Abfrage, die nur einmal pro Monat ausgeführt wird.

Verwenden Sie eine Art Profiler-Tool, um herauszufinden, was wirklich an und von der Datenbank gesendet wird. Ich kann mich an eine Zeit in der Vergangenheit erinnern, wo wir nicht herausfinden konnten, warum die Seite so langsam zu laden war, wenn die gespeicherte Prozedur schnell war und durch Profilermittlung herausgefunden wurde, dass die Webseite viele Male statt einmal nach der Abfrage gefragt hat.

Der Profiler hilft Ihnen auch herauszufinden, wer wen blockiert. Einige Abfragen, die beim Ausführen allein schnell ausgeführt werden, können aufgrund von Sperren aus anderen Abfragen sehr langsam werden.

Auf dieser Seite gibt es bereits viele Leistungsfragen, aber mir fällt auf, dass fast alle sehr problemspezifisch und ziemlich eng sind. Und fast alle wiederholen den Hinweis, um eine vorzeitige Optimierung zu vermeiden.

Angenommen:

  • Der Code funktioniert bereits korrekt
  • Die gewählten Algorithmen sind für die Umstände des Problems bereits optimal
  • der Code wurde gemessen und die störenden Routinen wurden isoliert
  • Alle Optimierungsversuche werden ebenfalls gemessen, um sicherzustellen, dass sie nicht schlechter werden

Was ich hier suche, sind Strategien und Tricks, um die letzten paar Prozent in einem kritischen Algorithmus herauszuholen, wenn es nichts anderes mehr zu tun gibt, als was immer es braucht.

Versuchen Sie im Idealfall, sprachunabhängige Antworten zu geben, und weisen Sie gegebenenfalls auf die vorgeschlagenen Strategien hin.

Ich füge eine Antwort mit meinen eigenen anfänglichen Vorschlägen hinzu und freue mich auf alles, was die Stack Overflow-Community sonst noch denken kann.


Der wichtigste limitierende Faktor ist heute die begrenzte Speicherbandbreite . Multicores machen dies nur noch schlimmer, da die Bandbreite zwischen Kernen geteilt wird. Auch die begrenzte Chipfläche, die zum Implementieren von Cachespeichern vorgesehen ist, wird ebenfalls zwischen den Kernen und Threads aufgeteilt, was dieses Problem noch weiter verschlimmert. Schließlich erhöht sich auch die Interchip-Signalisierung, die benötigt wird, um die verschiedenen Caches kohärent zu halten, mit einer erhöhten Anzahl von Kernen. Dies fügt auch eine Strafe hinzu.

Dies sind die Effekte, die Sie verwalten müssen. Manchmal durch Micro-Verwaltung Ihres Codes, aber manchmal durch sorgfältige Prüfung und Refactoring.

Viele Kommentare erwähnen bereits Cache-freundlichen Code. Es gibt mindestens zwei verschiedene Geschmacksrichtungen:

  • Vermeiden Sie Speicherabruflatenzen.
  • Niedrigerer Speicherbusdruck (Bandbreite).

Das erste Problem hat speziell damit zu tun, Ihre Datenzugriffsmuster regelmäßiger zu machen, damit der Hardware-Vorabrufer effizient arbeiten kann. Vermeiden Sie eine dynamische Speicherzuordnung, die Ihre Datenobjekte im Speicher verteilt. Verwenden Sie lineare Container anstelle von verknüpften Listen, Hashes und Bäumen.

Das zweite Problem hat mit der Verbesserung der Datenwiederverwendung zu tun. Ändern Sie Ihre Algorithmen so, dass sie an Teilmengen Ihrer Daten arbeiten, die in den verfügbaren Cache passen, und diese Daten so weit wie möglich wiederverwenden, während sie sich noch im Cache befinden.

Packen Sie die Daten enger und stellen Sie sicher, dass Sie alle Daten in den Cache-Zeilen in den Hot-Loops verwenden, um diese anderen Effekte zu vermeiden und weitere brauchbare Daten im Cache hinzufügen zu können.


Ich verbringe den größten Teil meines Lebens an genau diesem Ort. Die breiten Striche sollen deinen Profiler laufen lassen und es aufzeichnen lassen:

  • Cache vermisst . Der Daten-Cache ist die häufigste Quelle von Blockaden in den meisten Programmen. Verbessern Sie die Cache-Trefferrate, indem Sie anstößige Datenstrukturen reorganisieren, um eine bessere Lokalität zu erhalten. Packstrukturen und numerische Typen herunter, um verschwendete Bytes zu eliminieren (und somit Cache-Fetches zu verschwenden); Prefetch-Daten, wo immer möglich, um Ställe zu reduzieren.
  • Load-Hit-Stores . Compiler-Annahmen über Zeiger-Aliasing und Fälle, in denen Daten zwischen getrennten Registersätzen über den Speicher verschoben werden, können ein bestimmtes pathologisches Verhalten verursachen, das dazu führt, dass die gesamte CPU-Pipeline bei einem Ladevorgang gelöscht wird. Suchen Sie nach Stellen, an denen Floats, Vektoren und Ints zueinander geworfen werden, und eliminieren Sie diese. Verwenden Sie __restrict um dem Compiler das Aliasing zu versprechen.
  • Mikrocodierte Operationen . Die meisten Prozessoren verfügen über einige Operationen, die nicht pipelineed sein können, aber stattdessen führen Sie eine kleine Unterroutine aus, die in ROM gespeichert wird. Beispiele auf dem PowerPC sind Ganzzahl-Multiplikation, Dividieren und Verschiebung um die Variable Betrag. Das Problem ist, dass die gesamte Pipeline während der Ausführung dieser Operation gestoppt wird. Versuchen Sie, die Verwendung dieser Operationen zu eliminieren oder zumindest in ihre einzelnen Pipeline-Operationen zu zerlegen, damit Sie den Superskalar-Dispatch für den Rest Ihres Programms nutzen können.
  • Niederlassungsmispredicts . Auch diese leeren die Pipeline. Suchen Sie nach Fällen, in denen die CPU nach einer Verzweigung viel Zeit für das Auffüllen der Pipe benötigt, und verwenden Sie Verzweigungshinweis, falls vorhanden, damit sie häufiger korrekt vorhersagen kann. Oder noch besser: Ersetzen Sie Verzweigungen, wo immer es möglich ist, durch bedingte Bewegungen, insbesondere nach Gleitkommaoperationen, da ihre Pipe normalerweise tiefer ist und die Zustandsflags nach fcmp lesen, kann einen Stillstand verursachen.
  • Sequenzielle Gleitkomma-Ops . Mach diese SIMD.

Und noch etwas, was ich gerne mache:

  • Legen Sie Ihren Compiler fest, um Baugruppenlisten auszugeben, und sehen Sie sich an, was er für die Hotspot-Funktionen in Ihrem Code ausgibt . All diese cleveren Optimierungen, die "ein guter Compiler automatisch für Sie erledigen kann"? Chancen sind Ihre tatsächlichen Compiler tut sie nicht. Ich habe gesehen, dass GCC wirklich WTF-Code ausstrahlt.

OK, Sie definieren das Problem dahin, wo es scheint, dass es nicht viel Raum für Verbesserungen gibt. Das ist ziemlich selten, nach meiner Erfahrung. Ich habe versucht, dies in einem Artikel von Dr. Dobbs im November 1993 zu erklären, indem ich von einem konventionell gut konzipierten, nicht-trivialen Programm ohne offensichtliche Verschwendung ausgegangen bin und eine Reihe von Optimierungen durchführte, bis seine Wanduhrzeit von 48 Stunden reduziert wurde Sekunden bis 1,1 Sekunden, und die Quellcode-Größe wurde um einen Faktor von 4 reduziert. Mein Diagnose-Tool war dies . Die Reihenfolge der Änderungen war dies:

  • Das erste gefundene Problem war die Verwendung von Listenclustern (jetzt "Iteratoren" und "Containerklassen" genannt), die mehr als die Hälfte der Zeit ausmachen. Diese wurden durch einen relativ einfachen Code ersetzt, der die Zeit auf 20 Sekunden reduziert.

  • Jetzt ist der größte Zeitnehmer mehr Listenbildung. Als Prozentsatz war es vorher nicht so groß, aber jetzt liegt es daran, dass das größere Problem beseitigt wurde. Ich finde einen Weg, um es zu beschleunigen, und die Zeit fällt auf 17 Sekunden.

  • Jetzt ist es schwerer, offensichtliche Schuldige zu finden, aber es gibt ein paar kleinere, bei denen ich etwas tun kann, und die Zeit fällt auf 13 Sekunden.

Jetzt scheint ich eine Wand getroffen zu haben. Die Samples sagen mir genau, was es tut, aber ich kann nichts finden, was ich verbessern kann. Dann überlege ich das grundlegende Design des Programms, seine transaktionsorientierte Struktur und frage, ob alle Listensuchen, die es ausführt, tatsächlich von den Anforderungen des Problems abhängig sind.

Dann stieß ich auf ein Re-Design, bei dem der Programmcode tatsächlich (über Präprozessor-Makros) aus einem kleineren Satz von Quellen erzeugt wird, und in dem das Programm nicht ständig Dinge ermittelt, von denen der Programmierer weiß, dass sie ziemlich vorhersehbar sind. Mit anderen Worten, interpretiere die Reihenfolge der Dinge nicht, "kompiliere" sie.

  • Dieses Redesign erfolgt, indem der Quellcode um den Faktor 4 verkleinert wird und die Zeit auf 10 Sekunden reduziert wird.

Nun, weil es so schnell wird, ist es schwer zu testen, also gebe ich 10 Mal mehr Arbeit, aber die folgenden Zeiten basieren auf der ursprünglichen Arbeitslast.

  • Mehr Diagnose zeigt, dass es Zeit in der Warteschlangenverwaltung verbringt. In-Lining reduziert die Zeit auf 7 Sekunden.

  • Jetzt ist ein großer Zeitnehmer der diagnostische Druck, den ich gemacht habe. Flush das - 4 Sekunden.

  • Jetzt sind die größten Zeitnehmer Anrufe zu malloc und frei . Objekte recyceln - 2,6 Sekunden.

  • Wenn ich weiter sample, finde ich immer noch Operationen, die nicht unbedingt notwendig sind - 1,1 Sekunden.

Gesamtbeschleunigungsfaktor: 43.6

Jetzt sind keine zwei Programme gleich, aber in Nicht-Spielzeug-Software habe ich immer eine solche Entwicklung gesehen. Zuerst bekommst du die einfachen Sachen und dann die schwierigeren, bis du zu einem Punkt abnehmender Erträge kommst. Dann kann die gewonnene Erkenntnis zu einem Redesign führen und eine neue Runde von Beschleunigungen starten, bis Sie wieder abnehmende Renditen erzielen. Jetzt ist es der Punkt, an dem es Sinn machen könnte, sich zu fragen, ob ++i oder i++ oder for(;;) oder while(1) schneller sind: die Arten von Fragen, die ich so oft auf SO sehe.

PS Es mag sich wundern, warum ich keinen Profiler benutzt habe. Die Antwort ist, dass fast jedes dieser "Probleme" eine Funktionsaufruf-Site war, bei der die Stichproben stapelweise lokalisiert wurden. Profiler kommen selbst heute noch kaum auf die Idee, dass Anweisungen und Anweisungen zum Aufrufen wichtiger sind, als die ganzen Funktionen zu finden und leichter zu beheben. Ich habe dafür einen Profiler gebaut, aber für eine echte, dreckige Intimität mit dem, was der Code tut, gibt es keinen Ersatz dafür, dass man seine Finger richtig dabei hat. Es ist kein Problem, dass die Anzahl der Proben gering ist, da keines der gefundenen Probleme so klein ist, dass sie leicht übersehen werden können.

HINZUGEFÜGT: jerryjvl hat einige Beispiele angefordert. Hier ist das erste Problem. Es besteht aus einer kleinen Anzahl von separaten Codezeilen, die zusammen die Hälfte der Zeit beanspruchen:

 /* IF ALL TASKS DONE, SEND ITC_ACKOP, AND DELETE OP */
if (ptop->current_task >= ILST_LENGTH(ptop->tasklist){
. . .
/* FOR EACH OPERATION REQUEST */
for ( ptop = ILST_FIRST(oplist); ptop != NULL; ptop = ILST_NEXT(oplist, ptop)){
. . .
/* GET CURRENT TASK */
ptask = ILST_NTH(ptop->tasklist, ptop->current_task)

Diese verwendeten den Listencluster ILST (ähnlich einer Listenklasse). Sie werden in der üblichen Weise implementiert, wobei "Information Hiding" bedeutet, dass die Benutzer der Klasse sich nicht darum kümmern sollten, wie sie implementiert wurden. Als diese Zeilen geschrieben wurden (von ungefähr 800 Codezeilen), wurde nicht der Gedanke vertreten, dass dies ein "Flaschenhals" sein könnte (ich hasse dieses Wort). Sie sind einfach der empfohlene Weg, Dinge zu tun. Es ist im Nachhinein leicht zu sagen, dass diese hätten vermieden werden sollen, aber nach meiner Erfahrung sind alle Leistungsprobleme so. Im Allgemeinen ist es gut zu versuchen, Leistungsprobleme zu vermeiden. Es ist sogar besser, diejenigen zu finden und zu reparieren, die geschaffen wurden, obwohl sie (im Nachhinein) hätte vermieden werden sollen. Ich hoffe das gibt ein bisschen den Geschmack.

Hier ist das zweite Problem, in zwei getrennten Zeilen:

 /* ADD TASK TO TASK LIST */ 
ILST_APPEND(ptop->tasklist, ptask)
. . .
/* ADD TRANSACTION TO TRANSACTION QUEUE */
ILST_APPEND(trnque, ptrn)

Diese Listen werden erstellt, indem Elemente an ihre Enden angehängt werden. (Die Lösung bestand darin, die Elemente in Arrays zu sammeln und die Listen auf einmal zu erstellen.) Das Interessante ist, dass diese Anweisungen nur 3/48 der ursprünglichen Zeit kosten (also auf dem Aufruf-Stack waren), also waren sie nicht drin Tatsache, ein großes Problem am Anfang . Nach dem Entfernen des ersten Problems kosteten sie jedoch 3/20 der Zeit und waren nun ein "größerer Fisch". Im Allgemeinen geht es so.

Ich könnte hinzufügen, dass dieses Projekt von einem echten Projekt, an dem ich beteiligt war, abgeleitet wurde. In diesem Projekt waren die Leistungsprobleme viel dramatischer (wie die Beschleunigungen), wie das Aufrufen einer Datenbankzugriffsroutine innerhalb einer inneren Schleife, um zu sehen, ob eine Aufgabe beendet wurde.

REFERENZ HINZUGEFÜGT: Der Quellcode, sowohl originell als auch neu gestaltet, kann in www.ddj.com , für 1993, in der Datei 9311.zip, den Dateien slug.asc und slug.zip gefunden werden.

EDIT 2011/11/26: Es gibt jetzt ein Sourceforge-Projekt, das Quellcode in Visual C ++ und eine detaillierte Beschreibung dessen enthält, wie es eingestellt wurde. Es geht nur durch die erste Hälfte des oben beschriebenen Szenarios, und es folgt nicht genau der gleichen Sequenz, aber immer noch eine Beschleunigung um 2-3 Größenordnungen.


Sie sollten wahrscheinlich die "Google-Perspektive" berücksichtigen, dh bestimmen, wie Ihre Anwendung weitgehend parallelisiert und gleichzeitig ausgeführt werden kann. Dies wird unweigerlich auch bedeuten, dass Sie Ihre Anwendung über mehrere Maschinen und Netzwerke hinweg verteilen müssen, damit sie nahezu linear skalieren kann mit der Hardware, die Sie darauf werfen.

Auf der anderen Seite, die Google-Leute sind auch dafür bekannt, viel Personal und Ressourcen zu werfen, um einige der Probleme in Projekten, Tools und Infrastruktur zu lösen, wie zum Beispiel ganze Programmoptimierung für gcc durch ein engagiertes Team von Ingenieuren Hacking von gcc Internals, um es auf Google-typische Anwendungsszenarien vorzubereiten.

Ebenso bedeutet das Profiling einer Anwendung nicht mehr nur eine Profilierung des Programmcodes, sondern auch aller umliegenden Systeme und Infrastrukturen (Netzwerke, Switches, Server, RAID-Arrays), um Redundanzen und Optimierungspotenziale aus Sicht eines Systems zu identifizieren.


Vorschläge:

  • Vorberechnen statt neu berechnen : Alle Schleifen oder wiederholten Aufrufe, die Berechnungen enthalten, die einen relativ begrenzten Bereich von Eingaben enthalten, sollten eine Suche (Array oder Wörterbuch) durchführen, die das Ergebnis dieser Berechnung für alle Werte im gültigen Bereich enthält Eingaben. Verwenden Sie dann stattdessen eine einfache Suche innerhalb des Algorithmus.
    Downside : Wenn nur wenige der vorberechneten Werte tatsächlich verwendet werden, kann dies die Situation verschlimmern, auch das Nachschlagen kann erheblichen Speicherbedarf haben.
  • Verwenden Sie keine Bibliotheksmethoden : Die meisten Bibliotheken müssen so geschrieben sein, dass sie in einem breiten Spektrum von Szenarien korrekt funktionieren und Null-Überprüfungen von Parametern usw. durchführen. Durch die Neuimplementierung einer Methode können Sie eine Menge Logik entfernen gilt nicht genau unter den Umständen, die Sie verwenden.
    Downside : Das Schreiben von zusätzlichem Code bedeutet mehr Fläche für Bugs.
  • Benutze Bibliotheksmethoden : um mir selbst zu widersprechen, werden Sprachbibliotheken von Leuten geschrieben, die viel schlauer sind als du oder ich; Wahrscheinlich haben sie es besser und schneller gemacht. Implementiere es nicht selbst, es sei denn, du kannst es schneller machen (zB: immer messen!)
  • Cheat : In einigen Fällen, obwohl eine genaue Berechnung für Ihr Problem existieren kann, benötigen Sie möglicherweise nicht "genau", manchmal eine Annäherung kann "gut genug" und viel schneller in der Abmachung sein. Frage dich, ist es wirklich wichtig, ob die Antwort um 1% ist? 5%? sogar 10%?
    Downside : Nun ... die Antwort wird nicht genau sein.

Wenn Sie die Leistung nicht mehr verbessern können - sehen Sie, ob Sie stattdessen die wahrgenommene Leistung verbessern können.

Sie sind möglicherweise nicht in der Lage, Ihren fooCalc-Algorithmus schneller zu machen, aber oft gibt es Möglichkeiten, Ihre Anwendung auf den Benutzer besser reagieren zu lassen.

Ein paar Beispiele:

  • antizipieren, was der Benutzer anfordern wird und fangen an, vorher daran zu arbeiten
  • Die Ergebnisse werden angezeigt, sobald sie eingehen, und nicht alle gleichzeitig am Ende
  • Genaue Fortschrittsanzeige

Dies wird Ihr Programm nicht schneller machen, aber es könnte Ihre Benutzer glücklicher machen mit der Geschwindigkeit, die Sie haben.


Wie in mehreren früheren Antworten erwähnt, erfahren Sie zunächst, was Ihre Leistung beeinträchtigt - sei es Speicher oder Prozessor oder Netzwerk oder Datenbank oder etwas anderes. Abhängig davon ...

  • ... wenn es um Speicher geht - finde eines der Bücher, die Knuth vor langer Zeit geschrieben hat, eine der "The Art of Computer Programming" -Serien. Höchstwahrscheinlich geht es um Sortieren und Suchen - wenn mein Gedächtnis falsch ist, dann müssen Sie herausfinden, in welchen Gesprächen er über den Umgang mit langsamen Banddatenspeichern spricht. Verwandle dein Speicher / Tape- Paar in dein Cache / Hauptspeicher-Paar (oder in ein Paar L1 / L2-Cache). Studieren Sie alle Tricks, die er beschreibt - wenn Sie nicht etwas finden, das Ihr Problem löst, dann beauftragen Sie einen professionellen Informatiker, um eine professionelle Recherche durchzuführen. Wenn Ihr Speicherproblem zufällig bei FFT auftritt (Cache-Fehler bei bit-umgekehrten Indizes bei Radix-2-Falter), dann beauftragen Sie keinen Wissenschaftler - stattdessen können Sie die Pässe eins nach dem anderen manuell optimieren, bis Sie gewinnen oder gewinnen zur Sackgasse. Du hast gesagt, bis zu den letzten paar Prozent ausdrücken, oder? Wenn es nur wenige sind, werden Sie höchstwahrscheinlich gewinnen.

  • ... wenn es Prozessor ist - Wechsel zur Assemblersprache. Study Prozessor-Spezifikation - was dauert Ticks , VLIW, SIMD. Funktionsaufrufe sind höchstwahrscheinlich ersetzbare Tick-Esser. Schleifentransformationen lernen - pipeline, entrollen. Multiplizierungen und Divisionen könnten durch Bit-Verschiebungen ersetzbar / interpolierbar sein (Multiplikationen mit kleinen ganzen Zahlen können durch Zusätze ersetzbar sein). Versuche Tricks mit kürzeren Daten - wenn du Glück hast, könnte eine Anweisung mit 64 Bits austauschbar sein mit zwei auf 32 oder sogar 4 auf 16 oder 8 auf 8 Bits. Probieren Sie auch längere Daten aus - z. B. könnten Ihre Float-Berechnungen langsamer als die doppelten bei bestimmten Prozessoren ausfallen. Wenn Sie trigonometrisches Zeug haben, bekämpfen Sie es mit vorberechneten Tabellen; Beachten Sie auch, dass Sinus mit kleinem Wert durch diesen Wert ersetzt werden kann, wenn der Genauigkeitsverlust innerhalb der zulässigen Grenzen liegt.

  • ... wenn es sich um ein Netzwerk handelt - denken Sie daran, Daten zu komprimieren, die Sie übergeben. Ersetzen Sie die XML-Übertragung durch eine Binärdatei. Studienprotokolle. Versuchen Sie UDP anstelle von TCP, wenn Sie Datenverlust irgendwie behandeln können.

  • Wenn es eine Datenbank ist, gehen Sie in ein Datenbankforum und fragen Sie um Rat. In-Memory-Data-Grid, Optimierung des Abfrageplans usw. etc.

HTH :)


Did you know that a CAT6 cable is capable of 10x better shielding off extrenal inteferences than a default Cat5e UTP cable?

For any non-offline projects, while having best software and best hardware, if your throughoutput is weak, then that thin line is going to squeeze data and give you delays, albeit in milliseconds... but if you are talking about the last drops, that's a some drops gained, 24/7 for any packge sent or received.


Reduce variable sizes (in embedded systems)

If your variable size is larger than the word size on a specific architecture, it can have a significant effect on both code size and speed. For example, if you have a 16 bit system, and use a long int variable very often, and later realize that it can never get outside the range (−32.768 ... 32.767) consider reducing it to short int.

From my personal experience, if a program is ready or almost ready, but we realize it takes up about 110% or 120% of the target hardware's program memory, a quick normalization of variables usually solves the problem more often than not.

By this time, optimizing the algorithms or parts of the code itself can become frustratingly futile:

  • reorganize the whole structure and the program no longer works as intended, or at least you introduce a lot of bugs.
  • do some clever tricks: usually you spend a lot of time optimizing something, and discover no or very small decrease in code size, as the compiler would have optimized it anyway.

Many people make the mistake of having variables which exactly store the numerical value of a unit they use the variable for: for example, their variable time stores the exact number of milliseconds, even if only time steps of say 50 ms are relevant. Maybe if your variable represented 50 ms for each increment of one, you would be able to fit into a variable smaller or equal to the word size. On an 8 bit system, for example, even a simple addition of two 32-bit variables generates a fair amount of code, especially if you are low on registers, while 8 bit additions are both small and fast.


Tweak the OS and framework.

It may sound an overkill but think about it like this: Operating Systems and Frameworks are designed to do many things. Your application only does very specific things. If you could get the OS do to exactly what your application needs and have your application understand how the the framework (php,.net,java) works, you could get much better out of your hardware.

Facebook, for example, changed some kernel level thingys in Linux, changed how memcached works (for example they wrote a memcached proxy, and used udp instead of tcp ).

Another example for this is Window2008. Win2K8 has a version were you can install just the basic OS needed to run X applicaions (eg Web-Apps, Server Apps). This reduces much of the overhead that the OS have on running processes and gives you better performance.

Of course, you should always throw in more hardware as the first step...


Zwischenspeichern! Ein billiger Weg (in der Bemühung des Programmierers), fast alles schneller zu machen, besteht darin, eine Caching-Abstraktionsschicht zu jedem Datenbewegungsbereich Ihres Programms hinzuzufügen. Sei es I / O oder einfach nur die Erstellung von Objekten oder Strukturen. Oft ist es einfach, Caches zu Factory-Klassen und Reader / Writern hinzuzufügen.

Sometimes the cache will not gain you much, but it's an easy method to just add caching all over and then disable it where it doesn't help. I've often found this to gain huge performance without having to micro-analyse the code.


I've spent some time working on optimising client/server business systems operating over low-bandwidth and long-latency networks (eg satellite, remote, offshore), and been able to achieve some dramatic performance improvements with a fairly repeatable process.

  • Measure : Start by understanding the network's underlying capacity and topology. Talking to the relevant networking people in the business, and make use of basic tools such as ping and traceroute to establish (at a minimum) the network latency from each client location, during typical operational periods. Next, take accurate time measurements of specific end user functions that display the problematic symptoms. Record all of these measurements, along with their locations, dates and times. Consider building end-user "network performance testing" functionality into your client application, allowing your power users to participate in the process of improvement; empowering them like this can have a huge psychological impact when you're dealing with users frustrated by a poorly performing system.

  • Analyze : Using any and all logging methods available to establish exactly what data is being transmitted and received during the execution of the affected operations. Ideally, your application can capture data transmitted and received by both the client and the server. If these include timestamps as well, even better. If sufficient logging isn't available (eg closed system, or inability to deploy modifications into a production environment), use a network sniffer and make sure you really understand what's going on at the network level.

  • Cache : Look for cases where static or infrequently changed data is being transmitted repetitively and consider an appropriate caching strategy. Typical examples include "pick list" values or other "reference entities", which can be surprisingly large in some business applications. In many cases, users can accept that they must restart or refresh the application to update infrequently updated data, especially if it can shave significant time from the display of commonly used user interface elements. Make sure you understand the real behaviour of the caching elements already deployed - many common caching methods (eg HTTP ETag) still require a network round-trip to ensure consistency, and where network latency is expensive, you may be able to avoid it altogether with a different caching approach.

  • Parallelise : Look for sequential transactions that don't logically need to be issued strictly sequentially, and rework the system to issue them in parallel. I dealt with one case where an end-to-end request had an inherent network delay of ~2s, which was not a problem for a single transaction, but when 6 sequential 2s round trips were required before the user regained control of the client application, it became a huge source of frustration. Discovering that these transactions were in fact independent allowed them to be executed in parallel, reducing the end-user delay to very close to the cost of a single round trip.

  • Combine : Where sequential requests must be executed sequentially, look for opportunities to combine them into a single more comprehensive request. Typical examples include creation of new entities, followed by requests to relate those entities to other existing entities.

  • Compress : Look for opportunities to leverage compression of the payload, either by replacing a textual form with a binary one, or using actual compression technology. Many modern (ie within a decade) technology stacks support this almost transparently, so make sure it's configured. I have often been surprised by the significant impact of compression where it seemed clear that the problem was fundamentally latency rather than bandwidth, discovering after the fact that it allowed the transaction to fit within a single packet or otherwise avoid packet loss and therefore have an outsize impact on performance.

  • Repeat : Go back to the beginning and re-measure your operations (at the same locations and times) with the improvements in place, record and report your results. As with all optimisation, some problems may have been solved exposing others that now dominate.

In the steps above, I focus on the application related optimisation process, but of course you must ensure the underlying network itself is configured in the most efficient manner to support your application too. Engage the networking specialists in the business and determine if they're able to apply capacity improvements, QoS, network compression, or other techniques to address the problem. Usually, they will not understand your application's needs, so it's important that you're equipped (after the Analyse step) to discuss it with them, and also to make the business case for any costs you're going to be asking them to incur. I've encountered cases where erroneous network configuration caused the applications data to be transmitted over a slow satellite link rather than an overland link, simply because it was using a TCP port that was not "well known" by the networking specialists; obviously rectifying a problem like this can have a dramatic impact on performance, with no software code or configuration changes necessary at all.


If better hardware is an option then definitely go for that. Otherwise

  • Check you are using the best compiler and linker options.
  • If hotspot routine in different library to frequent caller, consider moving or cloning it to the callers module. Eliminates some of the call overhead and may improve cache hits (cf how AIX links strcpy() statically into separately linked shared objects). This could of course decrease cache hits also, which is why one measure.
  • See if there is any possibility of using a specialized version of the hotspot routine. Downside is more than one version to maintain.
  • Look at the assembler. If you think it could be better, consider why the compiler did not figure this out, and how you could help the compiler.
  • Consider: are you really using the best algorithm? Is it the best algorithm for your input size?

Last few % is a very CPU and application dependent thing....

  • cache architectures differ, some chips have on-chip RAM you can map directly, ARM's (sometimes) have a vector unit, SH4's a useful matrix opcode. Is there a GPU - maybe a shader is the way to go. TMS320 's are very sensitive to branches within loops (so separate loops and move conditions outside if possible).

The list goes on.... But these sorts of things really are the last resort...

Build for x86, and run Valgrind /Cachegrind against the code for proper performance profiling. Or Texas Instruments' CCStudio has a sweet profiler. Then you'll really know where to focus...


Not nearly as in depth or complex as previous answers, but here goes: (these are more beginner/intermediate level)

  • obvious: dry
  • run loops backwards so you're always comparing to 0 rather than a variable
  • use bitwise operators whenever you can
  • break repetitive code into modules/functions
  • cache objects
  • local variables have slight performance advantage
  • limit string manipulation as much as possible

The google way is one option "Cache it.. Whenever possible don't touch the disk"


Very difficult to give a generic answer to this question. It really depends on your problem domain and technical implementation. A general technique that is fairly language neutral: Identify code hotspots that cannot be eliminated, and hand-optimize assembler code.


  • Auf welcher Hardware laufen Sie? Können Sie plattformspezifische Optimierungen (z. B. Vektorisierung) verwenden?
  • Kannst du einen besseren Compiler bekommen? ZB Wechsel von GCC zu Intel?
  • Können Sie Ihren Algorithmus parallel ausführen lassen?
  • Können Sie Cache-Fehler reduzieren, indem Sie Daten reorganisieren?
  • Kannst du Asserts deaktivieren?
  • Micro-Optimierung für Ihren Compiler und Plattform. Im Stil von "an einem if / else, setze zuerst die gebräuchlichste Aussage"

  • Inline-Routinen (Eliminieren von Call / Return und Parameter Pushing)
  • Versuchen Sie, Tests / Schalter mit Tabellen-Look-Ups zu eliminieren (wenn sie schneller sind)
  • Lösen Sie die Schleifen (Duffs Gerät) bis zu dem Punkt, an dem sie in den CPU-Cache passen
  • Lokalisieren Sie den Speicherzugriff, um den Cache nicht zu überlasten
  • Lokalisieren Sie zugehörige Berechnungen, wenn der Optimierer dies nicht bereits tut
  • Beseitigen Sie Schleifeninvarianten, wenn der Optimierer dies nicht bereits tut




language-agnostic