[PYTHON] Un guide d'introduction à l'optimisation des performances des programmes

Présentation de principes et d'exemples polyvalents dans l'optimisation des performances du côté programme et susceptibles d'être rentables dans n'importe quelle langue ou domaine.

Conclusion

Gardez à l'esprit les trois principes suivants lors de l'optimisation et du développement de votre programme.

Le réglage et l'optimisation de l'architecture système, de la configuration matérielle, etc. sortent du cadre de cet article. (Les principes 2 et 3 peuvent être appliqués, mais nous n'irons pas aussi loin)

Contexte

Lorsque je suis impliqué dans divers développements de systèmes, j'ai l'impression qu'il y a étonnamment peu de gens qui sont conscients des performances dans la conception et le développement. Compte tenu de l'objectif et des exigences de performance de la cible de développement, même si c'est une partie qui semble clairement être un gros problème dans la vérification des performances, etc. plus tard, le développement se déroulera sans incident.

Bien entendu, la phrase «l'optimisation à mi-chemin est mauvaise et doit être réglée plus tard si nécessaire» est correcte. Cependant, cette maxime n'est-elle pas implicitement supposée ** faire des hypothèses et des prédictions sur certains goulots d'étranglement de performance au moment de la conception et du codage? C'est pourquoi je pense que la phrase «facilitons la mise au point plus tard en en faisant une méthode ou une modularisation où le réglage est susceptible d'être nécessaire» est souvent ajoutée.

Sans comprendre ce fait, j'ai procédé à des travaux tels que le codage et les tests unitaires sans faire d'hypothèses et de prédictions sur les goulots d'étranglement des performances, et lorsque les tests système ont été effectués, des goulots d'étranglement fatals se sont produits et j'ai fait des allers-retours. Il n'est pas rare de le voir disparaître. ** Il est normal de dire "le réglage des performances doit être effectué plus tard lorsque cela est nécessaire", mais si vous ne disposez pas des directives nécessaires (comment faire des hypothèses et des prévisions), des connaissances, des compétences, etc., ce n'est qu'une théorie théorique. .. Ce n'est pas différent du simple report du traitement des problèmes. ** **

NOTE: Si vous souhaitez développer quelque chose comme Intel DPDK (kit de développement pour le traitement de paquets réseau à très haut débit), vous devez naturellement mobiliser toutes les connaissances et compétences pour une optimisation plus avancée (ex. Parallélisation) , CPU etc. Affinité, accès au cache, lignes de cache, architecture CPU, options du compilateur, déchargement matériel, etc.). Mais à moins qu'il ne s'agisse d'un domaine aussi spécial, il existe d'autres optimisations de première priorité.

Caractéristiques du réglage des performances à introduire

Dans cet article, nous présenterons les principes avec les caractéristiques suivantes dans le réglage des performances et des exemples de techniques typiques.

--Peut être réalisé en modifiant le côté programme

Il existe de nombreuses optimisations plus spécialisées et avancées qui se spécialisent dans les langages de programmation, les architectures, le matériel, etc., mais les principes plus généraux doivent être suivis en premier.

NOTE: Parfois, je suis très familier avec les options d'optimisation du compilateur, le réglage des paramètres des noyaux Linux, etc., mais je ne connais / n'applique aucun des réglages de performance de base abordés dans cet article. Ces personnes essaient de résoudre le problème en réglant les performances de leur propre spécialité, donc en fonction du goulot d'étranglement des performances, elles ont tendance à effectuer le réglage des performances qui est extrêmement inefficace. Pour les ingénieurs qui ne connaissent pas très bien les environs, il y a un bonus qui répand le malentendu selon lequel "on ne peut pas y remédier car il est inutile de faire autant".

Principes de réglage des performances

Voici trois principes que je considère comme les plus importants. L'importance variera en fonction de la situation, mais fondamentalement, il est recommandé d'appliquer les principes dans l'ordre du haut.

NOTE: j'aimerais ajouter d'autres choses, mais s'il y en a trop, ce sera difficile à pratiquer, donc je m'en suis limité à trois.

Principe 1. Réduire le montant du calcul

L'une des choses les plus importantes dans le réglage des performances est de réduire la quantité de calcul dans le traitement du programme. Si vous ne connaissez pas le montant du calcul, la notation O, etc., veuillez vous référer aux articles suivants.

[Pour les débutants] Comment trouver le montant du calcul pour un programme Résumé de la façon de calculer l'ordre de montant de calcul! ~ D'où vient le journal ~

C'est un niveau de bon sens pour ceux qui se spécialisent dans les systèmes d'information et l'informatique, mais malheureusement, il y a peu de gens qui peuvent le pratiquer même s'ils le savent. Que vous écriviez un programme ou du SQL pour une base de données, prenez l'habitude de toujours garder à l'esprit la quantité de calcul.

NOTE: D'ailleurs, dans le cas de DB SQL, c'est déroutant car l'optimiseur peut changer le plan d'exécution interne en fonction du nombre d'enregistrements dans la table DB à ce moment, des informations statistiques, etc. Pour cette raison, SQL qui fonctionnait initialement avec un montant de calcul O (N) peut devenir O (N ^ 2) en raison d'une précision insuffisante des statistiques DB, par exemple.

En règle générale, ** Si la quantité de calcul est O (N) ou plus, méfiez-vous du fait que cela peut être un facteur qui nuit aux performances. Il est recommandé de vérifier avec la connaissance de **.

--Traitement des boucles --Traitement sécurisé

Exemple de critères de jugement

--Le montant du calcul est O (N ^ 2) ou plus, le nombre d'éléments de traitement N = des milliers à des dizaines de milliers ou plus

Supplément: Coefficient du montant de calcul

Par exemple, si le nombre de calculs est calculé par la formule «3 * n», le montant du calcul sera O (N). Vous pouvez ignorer le coefficient de 3.

Cependant, il est important d'être conscient de ce coefficient lors de l'examen de la performance du programme. Par exemple, si le nombre de calculs pour «10 * n» est de 10 secondes, le temps de traitement peut être amélioré à 1 seconde si la partie coefficient peut être définie sur 1. Bien sûr, cela dépend des exigences de l'objectif de développement, mais je pense qu'il y a suffisamment de cas où l'amélioration de cette partie de coefficient est extrêmement importante.

Le principe 2 et le principe 3, qui seront décrits plus loin, peuvent également être utilisés pour réduire cette partie de coefficient.

Principe 2. Traitement par lots à coût élevé

Compte tenu du traitement effectué par le programme et des performances requises (ex. Temps de traitement), il peut y avoir un traitement coûteux qui constitue un obstacle majeur à la satisfaction des performances requises. Il est fort probable qu'un tel ** traitement coûteux sera effectué plusieurs fois, alors envisagez de tout exécuter en même temps (par lots) **.

Que ce soit ou non considéré comme un processus à coût élevé est au cas par cas. Cependant, les processus suivants sont susceptibles d'être des «processus coûteux qui entravent les performances requises», c'est pourquoi nous vous recommandons de les considérer comme des candidats à des processus coûteux.

Comme mentionné précédemment, même si cela est susceptible d'être un obstacle, cela peut ne pas être un problème dans certaines situations. Par exemple, considérez trois modèles lorsque "SQL INSERT (cela prend 1 ms à chaque fois)", qui est le terme de contrôle d'un certain processus, est exécuté.

--Effectuez SQL INSERT au plus 10 fois dans le processus qui doit être effectué dans les 3 secondes --Depuis 1ms * 10 = 10ms, il est extrêmement improbable que cela devienne un goulot d'étranglement des performances. ** Pas de problème **

Comme vous pouvez le voir à partir des trois modèles ci-dessus, le fait que cela devienne ou non un obstacle dépend des exigences de performance et du nombre d'éléments de traitement. Pour cette raison, il est important de faire une estimation grossière de ce qui peut être un obstacle (candidat à un traitement à coût élevé) et de déterminer grossièrement s'il existe ou non un problème réel **. est. Si vous ne pouvez pas conclure qu'il n'y a pas de problème, cette partie sera frappée comme point de confirmation.

Une fois que vous avez identifié le processus à coût élevé, vous devrez réfléchir à la manière de mettre en lots cette pièce. En général, les traitements coûteux tels que ceux mentionnés ci-dessus ont une forme d'exécution collective et, lorsqu'ils sont exécutés collectivement, le temps de traitement peut souvent être considérablement réduit. Si vous ne disposez pas d'un tel moyen, vous devrez peut-être demander à l'appelé de mettre en œuvre un moyen de tout faire ensemble.

NOTE: Les langages de script tels que Python et Ruby sont plusieurs à des dizaines de fois plus lents dans le traitement étape par étape que les langages compilés tels que C / C ++ / Java (pour JavaScript, en raison de l'évolution remarquable du moteur V8, etc., c'est un peu plus lent. Les choses sont différentes). Par conséquent, en fonction du contenu du traitement et des exigences de performance, le traitement étape par étape lui-même peut devenir apparent comme un traitement coûteux. Dans ce cas, utilisez autant que possible des "fonctions et bibliothèques standard implémentées pour un traitement à grande vitesse en langage C, etc." pour les traiter toutes ensemble. Par exemple, dans le cas de Python, la génération et le traitement d'énormes tableaux sont laissés dans la mesure du possible à des bibliothèques telles que Numpy.

Exemple de critères de jugement

—— Exécution de dizaines à des centaines de commandes externes --Plus de prudence est requise si le temps de traitement des commandes externes est long

Principe 3. Réutiliser les articles coûteux

Par exemple, lors du traitement d'un programme, je pense qu'il est souvent implémenté en utilisant des threads et diverses connexions (ex. HTTP, SQL). Si vous les utilisez fréquemment, en créer un nouveau à chaque fois et vous en débarrasser peut être un coût étonnamment élevé. Pour un tel ** coût élevé et fréquemment utilisé, s'il peut être réutilisé, envisagez un mécanisme pour le réutiliser **

Bien entendu, ce qui coûte cher dépend beaucoup de l'objectif du programme et des exigences de performance. C'est la même chose que le principe 2. Par exemple, si vous n'envoyez une requête HTTP qu'une fois toutes les 10 minutes, le mérite de réutiliser une connexion HTTP est petit, mais si vous l'envoyez des dizaines à des centaines de fois par seconde, le mérite de réutiliser une connexion HTTP est excellent. est. En fonction de l'utilisation et des exigences de performances du programme, il peut être coûteux d'allouer de la mémoire avec malloc et de la libérer gratuitement chaque fois que vous utilisez la mémoire. Dans ce cas, réallouez la zone mémoire elle-même. Le mérite d'introduire un mécanisme à utiliser est également grand.

Les objectifs de réutilisation typiques sont indiqués ci-dessous.

--Connexions telles que HTTP et DB

Exemple de critères de jugement

Flux d'application du principe

Le principe d'optimisation s'applique lorsque vous effectuez les opérations suivantes:

--Lors de la mise en œuvre

Vérifiez les points suivants lors du codage. S'il y a des principes applicables, cela peut être un lieu qui doit être optimisé plus tard.

―― Que chaque principe soit respecté ou non ――Les mesures suivantes seront prises pour les points de vie. --TODO commentaire, intégration de journaux

Au fait, plus la plage de frappe est fine, plus il faut de temps et d'efforts, donc il est normal de rassembler une gamme de "ceci et cela est suspect". Une façon consiste à subdiviser la plage et à identifier la zone à problème lorsque la plage devient en fait un facteur qui nuit aux performances.

Prenons les mesures suivantes pour les parties concernées. Même si vous optimisez chaque partie pertinente sans y penser, l'effet peut être faible et il peut ne s'agir que d'une optimisation partielle, nous ne nous préparerons donc qu'à une optimisation ultérieure.

Échantillon de réglage

Voici quelques exemples pour vous aider à réaliser chaque principe. Le code source est essentiellement écrit sur l'hypothèse de Python 3.6.

REMARQUE: l'environnement (ex. OS, hôte / VM / WSL) est différent à chaque fois lors de la comparaison des résultats de mesure, veuillez donc n'utiliser que des informations de référence.

Principe 1. Réduire le montant du calcul

Utiliser HashMap / Set etc. pour plusieurs boucles

Plusieurs boucles sont souvent utilisées dans des processus tels que la recherche d'une collection pour une autre. Cependant, étant donné que la quantité de calcul pour plusieurs boucles augmente à O (N ^ 2) et O (N ^ 3), cela devient immédiatement apparent comme un goulot d'étranglement des performances lorsque le nombre d'éléments de traitement augmente.

Dans un tel cas, vous pouvez réduire la quantité de calcul de O (N ^ 2) à O (N), par exemple, en créant HashMap ou Set à partir de l'une des collections et en l'utilisant pour la recherche. Vous pouvez penser que le coût de création d'un nouveau HashMap / Set pour le nombre d'éléments de la collection est un gaspillage, mais si vous regardez le nombre d'éléments de traitement dans le tableau ci-dessous, vous pouvez voir que le coût est extrêmement faible.

Nombre d'éléments O(N)×2 O(N^2)
10 20 100
100 200 10,000
10,000 20,000 100,000,000

Tout d'abord, un exemple de traitement en double boucle est présenté. (Le traitement tel que la mesure du temps d'exécution est omis)

bad_performance_no1.py


all_files = list(range(1, 100000))
target_files = list(range(50000, 60000))
matched_files = []

#Le montant du calcul est O(N^2) -> BAD
for f in all_files:
    if f in target_files:
        matched_files.append(f)

print(len(matched_files) 

Ce à quoi vous devez faire attention ici est la partie de ʻif f dans target_files: dans l'instruction for. Comme cette partie n'est pas une boucle, il semble que le processus soit terminé une fois, mais afin de vérifier "si le target_files de la liste contient l'élément s", N / 2 en moyenne jusqu'à ce qu'il trouve un élément correspondant Le traitement de la vérification des éléments sera effectué. La même chose s'applique à la méthode d'opération de collecte contains ()` etc. en Java. Pour cette raison, ** même si la syntaxe du programme ne comporte pas de boucles multiples, le traitement réel peut être équivalent à plusieurs boucles **.

Voici un exemple de création d'un ensemble à partir d'une liste pour améliorer la double boucle. (Le traitement tel que la mesure du temps d'exécution est omis)

good_performance_no1.py


NUM = 1000000
nums = list(range(1, NUM))
# TUNING:Créer un ensemble à partir de la liste avec la fonction d'ensemble
# NOTE: //Diviser par consiste à contenir un entier
expected_nums = set(range(1, NUM//2))
matched_nums = []

#Le montant du calcul est O(N)Amélioré à-> GOOD
start = time.time()
for f in nums:
    if f in expected_nums:
        matched_nums.append(f)

print(len(matched_nums) 

Le résultat de la comparaison de la partie de traitement de boucle est indiqué ci-dessous.

Montant du calcul Temps d'exécution
O(N^2) 8,228 ms
O(N) 4 ms

Lorsque le nombre d'éléments de traitement N = 100 000, la vitesse a été augmentée d'environ 2000 fois en améliorant la quantité de calcul à O (N). Lorsque le nombre d'enregistrements à traiter est de plusieurs dizaines de milliers ou plus, même un traitement en boucle aussi simple a un grand effet d'amélioration.

Principe 2. Traitement par lots à coût élevé

Résumer l'exécution en masse de commandes externes

Lorsque vous exécutez une commande externe dans un programme, plus vous l'exécutez, plus les performances seront mauvaises. En effet, l'exécution d'une commande externe est un processus coûteux qui implique la création de processus.

Ici, nous prendrons comme exemple un programme qui vise à "afficher la taille totale en octets de tous les fichiers sous / etc". Vous pouvez afficher la taille en octets du fichier spécifié en spécifiant l'option --bytes avec la commande wc.

NOTE: À propos, la plupart des langages de programmation à usage général, y compris Python, fournissent des fonctions standard pour obtenir le nombre d'octets dans un fichier. Par conséquent, il n'est pas nécessaire d'utiliser la commande wc. Par conséquent, dans cet exemple, lisez en supposant que «la commande wc, qui est une commande externe, est absolument nécessaire».

bad_performance_no_batch.py


import pathlib
from subprocess import check_output

cwp = pathlib.Path("/etc")
files = [f for f in cwp.glob("**/*") if f.is_file()]
    
total_byte = 0
for f in files:
    #Exécutez la commande wc pour chaque fichier->Extrêmement inefficace
    result = check_output(["wc", "--byte", str(f)])
    size, file_path = result.split()
    total_byte += int(size)
   
print("file num:", len(files))
print("total byte:", total_byte)

Les résultats sont affichés à la fin de cette section, mais comme la commande wc est exécutée pour chaque fichier, cela prendra plusieurs secondes à plusieurs dizaines de secondes ou plus si le nombre de fichiers cibles est de plusieurs centaines à plusieurs milliers ou plus. Par conséquent, il est nécessaire de concevoir des moyens de réduire autant que possible le nombre d'exécutions de la commande wc. Heureusement, la commande wc vous permet de spécifier plusieurs fichiers avec une seule exécution de commande. Par conséquent, le nombre d'exécutions de commandes wc peut être groupé du nombre de fichiers à un.

Un exemple de traitement par lots est présenté ci-dessous. (Le traitement tel que la mesure du temps d'exécution est omis)

REMARQUE: l'analyse des résultats de la commande wc dans l'exemple est plutôt grossière et bâclée. Veuillez ne pas imiter.

good_performance_with_batch.py


import pathlib
from subprocess import check_output
cwp = pathlib.Path("/etc")
files = [f for f in cwp.glob("**/*") if f.is_file()]

#Traitement par lots en passant tous les fichiers comme arguments à la commande wc
args = [str(f) for f in files]
# NOTE:En Python*Peut étendre la liste des arguments comme argument avec
result = check_output(["wc", "--byte", *args])
total_byte = int(str(result).split(r"\n")[-2].split()[0])
    
print("file num:", len(files))
print("total byte:", total_byte)
Traitement des commandes par lots Temps d'exécution
Aucun 12,600 ms
Oui 338 ms

Même si j'ai utilisé la même commande wc avec l'option --bytes, j'ai pu réduire le temps de traitement à environ 1/40 par traitement par lots.

Il existe des commandes Unix / Linux et diverses bibliothèques (ex. SQL, HTTP, E / S) qui fournissent un tel traitement par lots d'une manière ou d'une autre. Si vous avez un problème de performances, utilisez-le de manière positive.

En passant, pour ceux qui souhaitent poursuivre le traitement par lots de commandes externes, les articles suivants seront utiles. Le titre dit "script shell", mais il s'agit essentiellement d'un article sur la manière d'utiliser efficacement les commandes externes.

Pour garder les scripts shell des dizaines de milliers de fois plus lents —— filtre sans boucle Suite: Afin de ne pas ralentir le script shell de dizaines de milliers de fois —— Le tube est toujours rapide et facile à comprendre

Utiliser le tampon pour le traitement des E / S (= réduire le nombre d'appels système)

Dans les situations où les exigences de performances sont élevées, les appels système autour du traitement des E / S tels que la lecture et l'écriture de fichiers et l'envoi et la réception de réseaux ont tendance à devenir des goulots d'étranglement majeurs. Par conséquent, la mise en mémoire tampon du traitement des E / S est importante pour réduire les appels système.

REMARQUE: L'important est "que pouvons-nous faire pour réduire le nombre d'appels système?" Et nous utilisons la mise en mémoire tampon d'E / S comme moyen efficace.

Par exemple, lors de la lecture et de l'écriture de fichiers, adoptez l'approche de mise en mémoire tampon appropriée pour chaque langue.

--Pour Java: utilisez BufferedReader, BufferedWriter, etc.

Un exemple de Python est présenté ci-dessous. Dans le cas de Python, le tampon d'E / S est activé par défaut, vérifions donc l'effet en le désactivant intentionnellement. (Le traitement tel que la mesure du temps d'exécution est omis)

# buffering=Si 0 est spécifié, je/Le tampon O devient invalide
f = open("file.txt", "wb", buffering=0)
s = b"single line"
for i in range(500000):
    f.write(s)
f.close()

Les résultats de comparaison suivants montrent également que l'activation du tampon d'E / S a amélioré les performances du traitement d'écriture d'environ 10 fois.

Avec ou sans tampon Temps d'exécution
Aucun 2,223 ms
Oui 245 ms

Ne pas utiliser les boucles for, while (langage de type script)

En raison du mécanisme de fonctionnement, les langages de type script tels que Python, Ruby et Perl sont extrêmement lents par rapport aux langages de programmation tels que C et Java qui sont exécutés en tant que code natif. En particulier, il n'est pas rare que les opérations simples et le traitement en boucle soient des dizaines à des centaines de fois plus lents.

Dans les situations où les performances de traitement sont importantes même pour les langages de script, déléguons la partie correspondant au traitement en boucle par la méthode suivante.

--Utilisez les fonctions intégrées et les fonctions linguistiques --Expression d'inclusion de liste Python --Fonctions telles que mapper, filtrer, appliquer (implémentées de préférence en langage C / C ++) --Utiliser des bibliothèques et des modules implémentés en langage C, etc.

NOTE: Cela peut être un peu déroutant, mais c'est l'une des méthodes liées au principe 2 car "le traitement en boucle du langage de type script est traité par lots par l'implémentation du langage C". Il est important de pouvoir penser ainsi, donc je veux en être conscient au quotidien.

A titre d'exemple, comparons le cas où la somme des listes numériques est traitée par traitement en boucle en Python et le cas où elle est effectuée par la fonction sum. (Le traitement tel que la mesure du temps d'exécution est omis)

nums = range(50000000)

#Pour le traitement en boucle
total  = 0
for n in nums:
    total += n

#Pour la fonction somme
total = sum(nums)

Les résultats de la comparaison sont présentés ci-dessous. C'est environ 5 fois plus rapide.

Méthode de calcul Temps d'exécution
Traitement en boucle 3,339 ms
fonction somme 612 ms

La fonction de somme étant implémentée en langage C, elle est traitée à grande vitesse. Puisque la fonction de jointure et la fonction de carte sont également implémentées en langage C, il est possible d'éviter le traitement en boucle côté Python et d'accélérer en en faisant bon usage.

Principe 3. Réutiliser les articles coûteux

Sélectionnez la jointure de chaîne appropriée

Selon le langage de programmation, le processus de combinaison de chaînes qui combine plusieurs chaînes pour créer une seule chaîne peut être très coûteux.

Dans le cas de Python et Java, un nouvel objet chaîne de caractères est généré chaque fois qu'une chaîne de caractères est combinée, ce qui est très inutile. Pour cette raison, utilisez StringBuilder pour Java et join () pour Python. Pour plus de détails, reportez-vous aux articles suivants.

[Java] Comparaison de la vitesse de la jointure de chaîne Comment augmenter la vitesse de traitement de Python # 2: utilisez join () pour concaténer un grand nombre de chaînes

Au fait, depuis Java 7, il a été optimisé pour utiliser automatiquement StringBuilder uniquement pour la concaténation de chaînes sur une ligne comme s =" hello "+" s "+" !! ". Cependant, notez que cette optimisation ne s'applique pas au traitement de jointure de chaîne pour les variables en dehors de la boucle.

Réutiliser la même connexion

Si vous utilisez une bibliothèque HTTP lors de l'envoi d'une requête telle que HTTP, en fonction de la bibliothèque, une connexion sera créée et détruite pour chaque requête. Par exemple, requests.get () dans la bibliothèque de requets de Python correspond à ce modèle.

Si vous envoyez des dizaines à des centaines de requêtes par seconde, assurez-vous d'utiliser la même connexion avec une connexion persistante avec HTTP 1.1 ou version ultérieure.

Comparons le cas où Session est utilisée dans la bibliothèque de requêtes Python et le cas où elle n'est pas utilisée. (Le traitement tel que la mesure du temps d'exécution est omis)

import requests

NUM = 3000
url = 'http://localhost:8080/'

#Sans session
def without_session():
    for i in range(NUM):
        response = requests.get(URL)
        if response.status_code != 200:
            raise Exception("Error")

#Avec Session
def with_session():
    with requests.Session() as ses:
        for i in range(NUM):
            response = ses.get(URL)
            if response.status_code != 200:
                raise Exception("Error")

without_session()
with_session()

Les résultats de la comparaison sont présentés ci-dessous. S'il y a un serveur Web dans le même hôte (local), il est environ 1,2 fois plus rapide, et s'il existe un serveur Web sur Internet (Internet), il est environ 2 fois plus rapide. Vous pouvez voir que plus le coût de connexion au serveur est élevé, plus l'effet est important.

Montant du calcul Temps d'exécution
Internet +Pas de session 7,000 ms
Internet +Avec Session 3.769 ms
Local +Pas de session 6,606 ms
Local +Avec Session 5.538 ms

Autre

Sujets d'optimisation des performances qui ne sont pas directement liés aux principes.

Ne génère pas de traitement lors de la sortie du journal DEBUG

Si le journal DEBUG utilise autre chose qu'une chaîne de caractères fixe, même s'il est défini pour ne pas sortir le journal DEBUG au niveau du journal, la partie qui passe l'argument sera calculée, donc des coûts de traitement seront engagés. Surtout s'il existe un tel journal DEBUG à un endroit où il est exécuté des milliers ou des dizaines de milliers de fois par seconde, l'impact sur les performances sera très important.

Lorsque vous entrez la sortie du journal DEBUG qui implique un calcul, assurez-vous de combiner l'instruction if de la vérification du niveau de journal afin que le traitement du calcul ne se produise pas au niveau du journal où la sortie du journal DEBUG n'est pas effectuée.

l = [1, 2, 3, 4, 5]

# BAD:
#Effectue le calcul de la liste totale et la concaténation de chaînes quel que soit le niveau de journalisation
logging.debug("Sum=" + sum(l))

# GOOD:
if logging.isdebug():
    #Pas du tout implémenté pour le niveau DEBUG
    logging.debug("Sum=" + sum(l))    

# GOOD:Dans le cas d'une chaîne de caractères fixe, le calcul n'a pas lieu, donc c'est OK
logging.debug("Entered the function A")

REMARQUE: Ce n'est pas le cas pour les langages de programmation évalués différés.

Recommended Posts

Un guide d'introduction à l'optimisation des performances des programmes
Je veux faire un programme d'automatisation!
Comment utiliser l'API du guide des programmes NHK
Serveur Web pour remplacer Apache: optimisation des performances uWSGI
Programme pour affaiblir le japonais
J'ai créé un guide de programme original en utilisant l'API de guide de programme NHK.