Performances des applications
De nombreux développeurs tombent dans le piège de penser que l’optimisation des performances consiste à rendre chaque ligne de code aussi efficace que possible.ou choisir d’écrire l’ensemble de leur application dans le langage de programmation le plus rapide qu’ils puissent trouver.
C’est en fait le contraire. Vous commencez par les contraintes architecturales de l’application et vous les utilisez pour effectuer une analyse descendante jusqu’au niveau observé. “le plus lent” partie du programme. La implémentation de cette partie guide tous les autres choix de performances que vous devez effectuer. Tout ce qui n’est pas aussi lent que cette partie n’a pas besoin d’être optimisé davantage. Au lieu de cela, concentrez-vous sur l’expression humaine et la simplicité et la clarté de la mise en œuvre, pour les lecteurs non experts sur la SSDLC pour le reste du code de votre programme.
Vous pouvez répéter sur ce livre de jeux, mais je n’ai jamais eu besoin d’aller au-delà de 3 itérations dans ma carrière professionnelle.
Alors allez-y et utilisez un langage de programmation élégant comme Python3 ou Javascript/Typescriptet laissez les experts en la matière (PME) là-bas dans le monde open source vous donner puissant C/C++ Liaisons natives pour vos besoins spécifiques. Rien de ce que vous faites pour la logique métier n’a besoin de plus de vitesse que tout langage de programmation dynamique peut vous donner prêt à l’emploi.
Même un script bash sans dépendance est une solution pratique pour de nombreuses tâches de base. Voici celui que j’ai écrit pour l’entreprise de réalité augmentée Le saut magique Il y a quelques années, pour remplacer un OpenGrok service avec quelque chose qui exploite la parallélisation multiprocesseur avec xarges - P, et supports PCRE rechercher avec simplicité Emacs/Vim Liaisons.
https://github.com/joesuf4/home/blob/wsl/bin/pffxg.sh
Ce script est un ordre de grandeur plus rapide que les suspects habituels sur GitHub, qui étaient tous écrits dans des langages de programmation compilés et dactylographiés statiquement. Mais en identifiant le goulet d’étranglement exact dans bas (en boucle avec un volume élevé) fork+exec appels au milieu), et l’utilisation xarges au lieu de cela, vous obtenez un script qui ressemble beaucoup à celui-ci, avec l’algorithme de base implémenté dans 10 lignes de shell.
Il utilise également la communauté open source des PME de manière intelligente, au lieu de la façon dont l’autre “graisse récurative filtrée” implémentations sur GitHub. Au lieu d’adopter et de maintenir ma propre mise en œuvre (threaded) de trouver, xarges, et grep, je réutilise simplement les exécutables préinstallés que d’autres PME ont perfectionnés au cours des décennies en l’état, et j’utilise principalement des intégrations de shell pour le reste. Je n’ai pas besoin de maîtriser leurs implémentations, il suffit de réutiliser leurs CLIs. Je ne veux même pas les maîtriser, c’est leur bailliage. Les deltas de performances n’ont d’importance que s’ils sont de plusieurs secondes ou plus, compte tenu des cas d’utilisation attendus (humains) de l’application.
Le reste du script tire principalement parti des constructions bash shell, qui sont implémentées en pur C par les développeurs bash.
Pour voir le côté opposé, où tout est fait en interne, entièrement microoptimisé, et ne peut toujours pas battre ce script avec les options de recherche par défaut, et aucun système de mise en cache disponible, voici un bon exemple https://github.com/BurntSushi/ripgrep
Il suffit d’extraire le premier #performance #benchmark de cette page et de l’augmenter d’une taille d’arbre d’échantillon de jouet (sources de noyau de linux) à un arbre hétérogène qui est 23GB : (meilleures courses après 3 itérations ; LANG=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
Il est tout à fait idiot de microoptimiser quelque chose qui est profondément lié à l’état du cache du système de fichiers du noyau pour votre recherche. La variation des délais de performance est dominée par la vitesse d’accès au corpus de contenus des fichiers, et elle est d’un ordre de grandeur plus pertinente que tout autre facteur des résultats finaux. Être sur un NVMe aide, mais rien dans cet espace ne bat RAM lui-même.
C’est pourquoi le fait d’avoir un cache compressé en mémoire pour un grand corpus de fichiers stabilisera les délais de performances. Il est surprenant que personne d’autre n’ait pensé que c’était assez important pour soutenir.
Retirez le deuxième élément #performance #benchmark de cette page et augmentez-le comme précédemment (identique à 23GB arbre :
% 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
Réglé pffxg.sh est encore plus rapide, malgré tout le travail mis en microoptimisation ripgrep pour cela C-recherche de fichier.
La façon dont j’ai utilisé ce script avec AOSP était de planifier un référentiel synchronisation et une pffxg.sh lz4-cache compresséetmpfs courir tous les matins avant le travail (via crontab), avec PFFXG_CACHE=... défini dans mon ~/.pffxg.conf fichier. Ainsi tout pffxg.sh les appels que j’ai exécutés pendant la journée de travail utiliseraient le cache compressé dans tmpfs, quel que soit l’état du cache du système de fichiers du noyau à l’époque.
.25M LOC entre ripgrep et ugrep. 632 LOC pour pffxg.sh. Silencieux.
Parce que c’est un si petit programme shell, pffxg.sh peut vous donner des crochets puissants dans ses internes avec presque zéro effort. Même le grep La commande elle-même est personnalisable : toute commande que vous devez exécuter sur un corpus de fichiers sélectionné, qui peut accepter une liste de noms de fichiers ajoutés à la fin de ses arguments, est un jeu équitable. Voici un “nombre total de lignes dans MiLOC“ exercice sur le référentiel git du noyau linux :
% 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 version :
% 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
Ici, il est limité à C-files (même arborescence linux) :
% 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
et ripgrep version :
% 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
La vraie performance de l’application provient de l’équilibre, de la flexibilité et des techniques de programmation fonctionnelle ; elle ne provient pas de la fixation sur les tactiques de microoptimisation impératives dans les langages de programmation statiques compilés qui sont un atout pour travailler du point de vue de l’équilibre et de la flexibilité. Ces langages surestimés et impératifs sont d’excellentes cibles pour des domaines problématiques très spécifiques, mais ils sont terribles pour les performances des applications à l’échelle du système.
pffxg.sh n’est pas un produit, et ce n’est pas un argument de vente pour elle. C’est un exemple pour illustrer mon point d’une manière très dramatique. Si vous êtes familier avec la longue histoire des solutions filtrées-récursives-grep sur GitHub, ils sont tous basés sur l’idée que le problème avec l’original Andy Lester Perl implémentation ack, a été écrit dans Perl. Le seul vrai problème du point de vue de la performance était que Perl a été écrit par Andy, qui ne semblait pas avoir le moindre talent pour les concepts de performance des systèmes (comme cultiver le trouver travail de parallélisation à un C binaire), mais visait plutôt à la portabilité paresseuse en essayant de capturer l’ensemble du code en tant que Pure monothread Perl bizarrerie.
Que mille fleurs fleurissent, quoi qu’il en soit stupide !
