Anwendungsperformance
Viele Entwickler fallen in die Falle des Denkens, dass es bei der Leistungsoptimierung darum geht, jede Codezeile so effizient wie möglich zu machen.
Es ist eigentlich das Gegenteil. Sie beginnen mit den architektonischen Einschränkungen der Anwendung und verwenden sie, um einen Drilldown zum beobachteten “langsamsten” Teil des Programms durchzuführen. Die Implementierung dieses Teils führt alle anderen Performanceoptionen an, die Sie treffen müssen. Alles, was nicht so langsam ist wie dieser Teil, muss nicht weiter optimiert werden. Konzentrieren Sie sich stattdessen auf den menschlichen Ausdruck und die Einfachheit und Klarheit der Umsetzung, um nicht-expert Leser über die SSDLC
Sie können auf diesem Spielbuch iterieren, aber ich habe nie über 3 Iterationen in meiner beruflichen Karriere gehen müssen.
So gehen Sie voran und verwenden Sie eine elegante Programmiersprache wie Python3
oder Javascript
/Skript
, und lassen Sie sich von den Subject Matter Experts (SME) in der Open-Source-Welt mächtig machen C
/C++
Selbst ein abhängigkeitsfreies Bash-Skript ist eine praktikable Lösung für viele grundlegende Aufgaben. Hier ist einer, den ich für Augmented Reality geschrieben habe Magischer Sprung Vor Jahren, um ein ungeschicktes OpenGrok Service mit etwas, das Multiprozessor-Parallelisierung nutzt mit xargs -P
und Support PCRE Suche mit einfachen Emacs
/Vim
https://github.com/joesuf4/home/blob/wsl/bin/pffxg.sh
Dieses Skript ist eine Größenordnung schneller als die üblichen Verdächtigen auf GitHub, die alle in statischen, kompilierten Programmiersprachen geschrieben wurden. Aber durch die Identifizierung des genauen Engpasses in Basch
(Schleifen mit hohem Volumen) fork+exec
Aufrufe in der Mitte) und xargs
Stattdessen erhalten Sie ein Skript, das viel wie dieses aussieht, wobei der Kernalgorithmus in 10 Zeilen von Shell
Es verwendet auch die Open-Source-Community von SME auf intelligente Weise, anstatt wie die anderen “gefilterten rekursiven Grep”-Implementierungen auf GitHub. Anstatt meine eigene (Threaded) Implementierung intern zu übernehmen und zu pflegen suchen
, xargs
und Grep
, verwende ich nur die vorinstallierten ausführbaren Dateien, die andere SMEs über Jahrzehnte perfektioniert haben as-is. Ich muss ihre Implementierungen nicht beherrschen, sondern einfach wiederverwenden CLI
Um den gegenteiligen Tack zu sehen, bei dem alles intern durchgeführt wird, vollständig mikrooptimiert ist und dieses Skript immer noch nicht mit den Standardsuchoptionen übertroffen werden kann und kein Caching-System verfügbar ist, finden Sie hier ein schönes Beispiel https://github.com/BurntSushi/ripgrep
Nur um die erste #performance #benchmark von dieser Seite zu ziehen und sie von einer Spielzeugbeispielbaumgröße (Linux-Kernelquellen) auf einen heterogenen Baum zu skalieren, der 23GB
: (beste Ausführung nach 3 Iterationen; SPRACHE=en_US.UTF-8
% du -sh .
23G .
% time rg -uuniw '[A-Z]+_SUSPEND' | wc -l
6259
rg -uuniw '[A-Z]+_SUSPEND' 9.46s user 16.08s system 261% cpu 9.759 total
wc -l 0.00s user 0.07s system 0% cpu 9.759 total
% time pffxg.sh -- -wnE '[A-Z]+_SUSPEND' | wc -l
5855
pffxg.sh -- -wnE '[A-Z]+_SUSPEND' 16.66s user 2.68s system 429% cpu 4.501 total
wc -l 0.00s user 0.00s system 0% cpu 4.501 total
Es ist ziemlich albern, etwas zu mikrooptimieren, das eng mit dem Status des Dateisystemcaches des Kernels für Ihre Suche verbunden ist. Die Variation der Performance-Timings wird von der Zugriffsgeschwindigkeit auf den Inhalt der Dateien dominiert, und es ist eine Größenordnung relevanter als jeder andere Faktor für die Endergebnisse. Auf einem NVMe
hilft, aber nichts in diesem Raum schlägt RAM
Aus diesem Grund stabilisiert ein im Arbeitsspeicher komprimierter Cache für ein großes Korpus von Dateien die Performance-Timings. Es ist überraschend, dass niemand sonst dachte, dies sei wichtig genug, um zu unterstützen.
Nehmen Sie die zweite #performance #benchmark von dieser Seite, und skalieren Sie sie wie zuvor (gleich). 23GB
% time rg -tc -uuuiwn '[A-Z]+_SUSPEND' | wc -l
5629
rg -tc -uuuiwn '[A-Z]+_SUSPEND' 3.51s user 1.71s system 1141% cpu 0.457 total
wc -l 0.00s user 0.05s system 11% cpu 0.457 total
% time LANG=C pffxg.sh --cache /tmp/pffxg-$USER --workers 32 --cc -- -wE '[A-Z]+_SUSPEND' | wc -l
5628
LANG=C pffxg.sh --cache /tmp/pffxg-$USER --workers 32 --cc -- -wE 3.14s user 0.88s system 1055% cpu 0.381 total
wc -l 0.00s user 0.00s system 0% cpu 0.381 total
Abgestimmt pffxg.sh
ist immer noch schneller, trotz aller Arbeiten, die in die mikrooptimierende Ripgrep für diese C
Wie ich dieses Skript verwendet habe mit AOSP sollte eine Repository
Synchronisierung und eine nachfolgende pffxg.sh
ZOP
-compressed-cache Seed-to-SeedTmpfs
jeden Morgen vor der Arbeit laufen (via Crontab
), mit PFFXG_CACHE=...
Set in meinem ~/.pffxg.conf
Datei. So pffxg.sh
Aufrufe, die ich während des Arbeitstages ausgeführt habe, verwenden den komprimierten Cache in Tmpfs
.25M LOC zwischen Ripgrep
und ugrep. 632 LOC für pffxg.sh
Weil es so ein kleines Shell-Programm ist, pffxg.sh
können Sie leistungsstarke Haken in seine Innenräume mit fast null Aufwand geben. Sogar Grep
Befehl selbst ist anpassbar: Jeder Befehl, den Sie auf einem ausgewählten Korpus von Dateien ausführen müssen, der eine Liste von Dateinamen akzeptieren kann, die an das Ende seiner Argumente angehängt sind, ist ein faires Spiel. Hier ist eine “Gesamtpositionsanzahl in MiLOC
% time find * -type f | xargs wc -l | awk '{ $2 == "total" {a+=$1} END {print a/1024**2}'
28.451
find * -type f 0.00s user 0.06s system 2% cpu 2.733 total
xargs wc -l 0.53s user 1.02s system 54% cpu 2.853 total
awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.23s user 0.59s system 28% cpu 2.853 total
% time pffxg.sh --workers 8 --cmd wc --all -- -l | awk '{$2 == "total" {a+=$1} END {print a/1024**2}'
28.4506
pffxg.sh --workers 8 --cmd wc --all -- -l 0.92s user 0.66s system 826% cpu 0.192 total
awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.02s user 0.00s system 11% cpu 0.192 total
Ripgrep
% time rg -c \$ | awk -F : '{a+=$2} END {print a/1024**2}'
28.4284
rg -c \$ 2.12s user 2.19s system 276% cpu 1.564 total
awk -F : '{a+=$2} END {print a/1024**2}' 0.58s user 0.45s system 66% cpu 1.564 total
Hier ist es beschränkt auf C
% time pffxg.sh --workers 8 --cc --cmd wc -- -l | awk '$2 == "total" {a+=$1} END {print a/1024**2}'
25.3935
pffxg.sh --workers 8 --cc --cmd wc -- -l 0.76s user 0.54s system 734% cpu 0.177 total
awk '$2 == "total" {a+=$1} END {print a/1024**2}' 0.02s user 0.00s system 9% cpu 0.177 total
und die Ripgrep
% time rg -tc -c \$ | awk -F : '{a+=$2} END {print a/1024**2}'
25.3844
rg -tc -c \$ 3.49s user 1.54s system 441% cpu 1.140 total
awk -F : '{a+=$2} END {print a/1024**2}' 0.38s user 0.38s system 66% cpu 1.140 total
Die tatsächliche Anwendungsleistung kommt von Balance, Flexibilität und funktionalen Programmierungstechniken; sie kommt nicht von der Fixierung auf zwingende Mikrooptimierungstaktiken in statischen, kompilierten Programmiersprachen, die aus der Balance- und Flexibilitätsperspektive ein Bär sind. Solche überholten, zwingenden Sprachen sind großartige Ziele für sehr spezifische Problemdomänen, sind aber für die systemweite Anwendungsleistung schrecklich.
pffxg.sh
ist kein Produkt, und das ist kein Verkaufsargument dafür. Es ist ein Beispiel, um meinen Punkt auf eine sehr dramatische Weise zu veranschaulichen. Wenn Sie mit der langen Geschichte der gefilterten-rekursiven-grep-Lösungen auf GitHub vertraut sind, sind sie alle auf der Vorstellung, dass das Problem mit Andy Lesters Original Perl
Implementierung Bestätigenwar, dass es in Perl
. Das einzige wirkliche Problem aus Performance-Sicht war, dass die Perl
wurde von Andy geschrieben, der anscheinend keine Ahnung von Systemleistungskonzepten hatte (wie z.B. suchen
Parallelisierungsarbeit zu einem speziell entwickelten C
Binär), sondern zielte auf faule Portabilität ab, indem er versuchte, den gesamten Code als Single-Thread-Pure zu erfassen Perl
Mögen tausend Blumen blühen, egal wie dumm sie scheinen!