Performances des applications

[ARCHIVÉ] Dernière mise à jour par Joe Schaefer sur ven., 26 avr. 2024    source
 

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.

C’est en fait le contraire. Commencez par les contraintes architecturales de l’application et utilisez-les pour effectuer une analyse descendante jusqu’à la partie la plus lente observée 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 Palette SSD

Vous pouvez itérer sur ce playbook, 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/Descriptif, et laissez les experts en la matière (PME) dans le monde open source vous donner de puissants C/C++

Même un script bash sans dépendance est une solution pratique pour de nombreuses tâches de base. En voici un que j’ai écrit pour la société de réalité augmentée Saut magique Il y a quelques années, pour remplacer un OpenGrok service avec quelque chose qui exploite la parallélisation multiprocesseur avec xargs - P, et supports PCRE recherche simple Emacs/Vim

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 ont tous été écrits dans des langages de programmation compilés statiques. Mais en identifiant le goulot d’étranglement exact dans bas (boucle avec volume élevé fork+exec au milieu), et en utilisant 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 coque

Il utilise également la communauté open source des PME de manière intelligente, au lieu de la façon dont les autres implémentations “grappes récursives filtrées” sur GitHub l’ont fait. Au lieu d’adopter et de maintenir en interne ma propre mise en œuvre (threadée) de rechercher, xarges, et grp, je viens de réutiliser les exécutables préinstallés que d’autres PME perfectionnent depuis des décennies tels quels. Je n’ai pas besoin de maîtriser leurs implémentations, il suffit de réutiliser leurs CLI

Pour voir l’approche inverse, 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 bel exemple https://github.com/BurntSushi/ripgrep

Juste pour extraire le premier #performance #benchmark de cette page, et le mettre à l’échelle d’une taille d’arbre d’échantillon de jouet (sources de noyau de linux), à un arbre hétérogène qui est 23GB : (meilleures exécutions après 3 itérations ; LANGUE=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 assez stupide 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 temps de performance est dominée par la vitesse d’accès au corpus de contenu des fichiers, et c’est un ordre de grandeur plus pertinent que tout autre facteur pour les résultats finaux. Être sur un NVMe aide, mais rien dans cet espace ne bat RAM

C’est pourquoi le fait d’avoir un cache compressé en mémoire pour un grand nombre de fichiers stabilise les délais de performance. Il est surprenant que personne d’autre n’ait pensé que cela était assez important pour soutenir.

Retirez le deuxième #performance #benchmark de cette page et redimensionnez-le comme précédemment (le même 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

Un réglé pffxg.sh est encore plus rapide, malgré tout le travail mis en microoptimisation ripgrep pour cela C

La façon dont j’ai utilisé ce script avec AOSP était de planifier un référentiel synchronisation et une pffxg.sh lzop-graine de cache compressée àtmpfs courir tous les matins avant le travail (via crontab), avec PFFXG_CACHE=... dans mon ~/.pffxg.conf fichier. Ainsi n’importe lequel pffxg.sh les appels que j’ai exécutés tout au long de la journée de travail utilisent le cache compressé dans tmpfs

.25M LOC entre ripgrep et ugrep. 632 LOC pour pffxg.sh

Parce que c’est un petit programme shell, pffxg.sh peut vous donner des crochets puissants dans ses internes avec presque zéro effort. Même le grp 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

    % 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

Ici, il est limité à 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

et 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

Les performances applicatives réelles proviennent de l’équilibre, de la flexibilité et des techniques de programmation fonctionnelles ; elles ne proviennent pas de la fixation sur des tactiques de microoptimisation impératives dans des langages de programmation compilés statiques qui sont difficiles à utiliser du point de vue de l’équilibre et de la flexibilité. Ces langages impératifs surdimensionnés 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 argumentaire de vente pour lui. C’est un exemple pour illustrer mon propos d’une manière très dramatique. Si vous connaissez la longue histoire des solutions filtrées-récursives-grep sur GitHub, elles reposent toutes sur l’idée que le problème avec l’original d’Andy Lester Perle Implémentation accusé de réceptionétait-ce qu’il était écrit en Perle. Le seul vrai problème du point de vue de la performance était que le Perle Il a été écrit par Andy, qui ne semblait pas avoir le moindre talent pour les concepts de performance des systèmes (comme l’exploitation de rechercher un travail de parallélisation vers un projet spécifique C binaire), mais visait plutôt à la portabilité paresseuse en essayant de capturer l’ensemble du code en tant que Pure à un seul thread Perle

Que mille fleurs fleurissent, peu importe ce qu’elles paraissent idiotes !

Lien permanent 

 

   

Commentaires  


Pièces jointes  

Liens  


Index

 

  • Listes de diffusion — Ces adresses temporaires sont anathèmes à ezmlm-idxles systèmes d’abonnement et de modération … ven., 03 mai 2024

 


 

  • Git et non-répudiation — Il y a une distinction claire entre l’histoire “commit” et l’histoire “upload”. … ven., 03 mai 2024


 

  • La joie de DTrace — Mesurez deux fois, coupez une fois, avant de vous lancer dans un effort d’optimisation de code … mer., 17 avr. 2024

 


COVID-19 mars 2020

  • Croissance exponentielle et COVID-19 — Prenez votre temps avec la section mathématiques — Il est important d’être un consommateur averti de statistiques pertinentes pour la pandémie actuelle … lun., 30 janv. 2023

 

  • Sur le problème du spam… — Le meilleur plugin pour qpsmtpd, bien qu’il soit difficile de comprendre pourquoi … dim., 29 janv. 2023

 


Nid d'abeille hyperbolique


 

  • Fun avec htop — Fonctionnalités htop avancées sur les plates-formes Unix populaires … jeu., 19 janv. 2023

Architecture des informations

  • Architecture des informations — Toute la gamme de technologies pertinentes pour la conception, la présentation, les relations et les contraintes architecturales qui couvrent chaque URL que vous servez … lun., 11 mars 2024

goudronné et plumé


Dépendances de la langue anglaise


 

  • Le mouvement DevOps — La grande idée derrière le “mouvement” n’est pas simplement de donner aux développeurs plus de corde … ven., 26 avr. 2024

Année du Dragon