Lorsque votre VPS exécute plusieurs applications de service, mais que l'une d'elles occupe parfois toutes les ressources, ce qui rend impossible l'accès au serveur via ssh. Vous passez à l’utilisation d’un cluster Kubernetes et définissez des limites pour toutes les applications. Nous avons ensuite vu certaines applications redémarrer alors que le OOM-killer résolvait le problème de « fuite » de mémoire.
Bien sûr, le MOO n'est pas toujours un problème de fuite, cela peut aussi être un dépassement de ressources. Les problèmes de fuite sont très probablement causés par des erreurs de programme. Le sujet dont nous parlons aujourd'hui est de savoir comment éviter cette situation.
Une consommation excessive de ressources nuit au portefeuille, ce qui signifie que nous devons prendre des mesures immédiates.
Parlons maintenant de l'optimisation. J'espère que vous comprendrez pourquoi nous ne devrions pas optimiser prématurément !
Nous donnons maintenant quelques suggestions pratiques selon la classification standard des entités dans Go.
Essayez d'utiliser le troisième paramètre : <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">make([]T, 0, len)</span>
make([]T, 0, len)
Si le nombre exact d'éléments n'est pas connu et la tranche est de courte durée Oui, vous pouvez allouer une taille plus grande pour garantir que la tranche ne s'agrandit pas pendant l'exécution.
N'oubliez pas d'utiliser copyEssayez de ne pas utiliser append lors de la copie, par exemple lors de la fusion de deux tranches ou plus.
Itération correcteUne tranche contenant de nombreux éléments ou de gros éléments, utilisez pour pour obtenir un seul élément. De cette façon, les duplications inutiles seront évitées.
Multiplexage de tranchesSi une opération est effectuée sur la tranche entrante et renvoie un résultat modifié, nous pouvons le renvoyer. Cela évite une nouvelle allocation de mémoire.
🎜Si vous devez couper un petit morceau de la tranche et l'utiliser uniquement, la partie principale de la tranche sera également conservée. L'approche correcte consiste à utiliser une nouvelle copie de cette petite tranche et à lancer l'ancienne tranche au GC.
Si l'épissage des chaînes peut être effectué en une seule instruction, utilisez <code style="font-size: 14px;word-wrap: break-word;padding: 2px 4px;border-radius: 4px;margin: 0 2px;background-color: rgba(27,31,35,.05);font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;word-break: break-all;color: #000000;background: rgba(14, 210, 247, 0.15);"><span style="font-size: 15px;">+</span>
操作符。如果需要在循环中执行此操作,使用 <span style="font-size: 15px;">string.Builder</span>
,并使用它的 <span style="font-size: 15px;">Grow</span>
方法预先指定 <span style="font-size: 15px;">Builder</span>
的大小,减少内存分配次数。
string 和 []byte 在底层结构上非常相近,有时这两种类型之间可以通过强转换来避免内存分配。
可以池化字符串,从而帮助编译器只存储一次相同的字符串。
我们可以使用 map(级联)而不是复合键,我们可以使用字节切片。尽量不使用 <span style="font-size: 15px;">fmt</span>
+ opérateur. Si vous devez le faire en boucle, utilisez
🎜string.Builder🎜
🎜 et utilisez son 🎜🎜Grow🎜
🎜 la méthode est pré-spécifiée🎜🎜Builder🎜
🎜 taille pour réduire le nombre d'allocations de mémoire. 🎜🎜🎜Optimisation de la conversion🎜🎜🎜string et []byte sont très similaires dans la structure sous-jacente, et parfois une conversion forte peut être utilisée entre ces deux types pour éviter l'allocation de mémoire. 🎜🎜🎜String résident🎜🎜🎜 peut regrouper des chaînes, aidant ainsi le compilateur à stocker la même chaîne une seule fois. 🎜🎜🎜Éviter l'allocation🎜🎜🎜Nous pouvons utiliser map(cascade) au lieu de clés composites, nous pouvons utiliser des tranches d'octets. Essayez de ne pas utiliser 🎜🎜 fmt 🎜
🎜 package, car toutes ses méthodes utilisent la réflexion. 🎜🎜Nous comprenons que les petites structures ne comportent pas plus de 4 champs et pas plus d'une taille de mot machine.
Quelques scénarios de copie typiques
Le déréférencement coûte cher et nous devrions le faire le moins possible, surtout dans les boucles. Il perd également la possibilité d'utiliser des registres rapides.
Ce travail est optimisé par l'éditeur, ce qui veut dire qu'il est bon marché.
Nous pouvons réduire la taille de la structure elle-même en alignant la structure (en les disposant dans le bon ordre en fonction de la taille des champs).
Essayez d'écrire de petites fonctions qui peuvent être intégrées par le compilateur, ce sera plus rapide, encore plus rapide que d'intégrer vous-même le code dans la fonction. Cela est particulièrement vrai pour les chemins chauds.
Lesquels ne seront pas en ligne
Essayez d'utiliser de petits paramètres car leur réplication sera optimisée. Essayez de maintenir la réplication et la croissance de la pile équilibrées sur la charge du GC. Évitez un grand nombre de paramètres et laissez votre programme utiliser des registres rapides (leur nombre est limité).
Cela semble être plus efficace que de déclarer ces variables dans le corps de la fonction.
Aidez le compilateur à optimiser votre code, enregistrez les résultats intermédiaires, et il y aura ensuite plus d'options pour optimiser votre code.
Essayez de ne pas utiliser defer, ou du moins ne l'utilisez pas en boucle.
Évitez d'allouer de la mémoire dans le chemin chaud, en particulier les objets de courte durée. Créez les branches les plus courantes (si, switch)
Identique à la tranche, lors de l'initialisation de la carte, spécifiez sa taille.
struct{} n'est rien (ne prend pas de mémoire), il est donc très avantageux de l'utiliser lors du passage de signaux par exemple.
la carte ne peut que croître, pas rétrécir. Lorsque nous devons réinitialiser la carte, supprimer tous ses éléments ne nous aidera pas.
Si la carte ne contient pas de pointeurs, alors le GC ne perdra pas de temps précieux dessus. Les chaînes utilisent également des pointeurs, vous devez donc utiliser des tableaux d'octets au lieu de chaînes comme clés.
De même, nous ne voulons pas utiliser de pointeurs, mais nous pouvons utiliser une combinaison de carte et de tranche, en stockant les clés dans la carte et les valeurs dans la tranche. De cette façon, nous pouvons modifier la valeur sans restrictions.
N'oubliez pas que lorsque vous souhaitez attribuer une valeur à une interface, vous devez d'abord la copier quelque part, puis y coller le pointeur. La clé est de copier. Il s'avère que le coût du boxing et du déballage de l'interface sera à peu près le même que celui de l'allocation de la taille de la structure.
Dans certains cas, il n'y a pas d'allocation lors du boxing et du unboxing des interfaces. Par exemple, des valeurs petites ou booléennes pour les variables et les constantes, des structures avec un champ simple, des pointeurs (y compris carte, canal, fonction)
Comme ailleurs, essayez d'éviter les allocations inutiles. Par exemple attribuer une interface à une autre au lieu de boxer deux fois.
Évitez d'utiliser des interfaces dans les paramètres de fonction fréquemment appelés et renvoyez les résultats. Nous n'avons pas besoin d'opérations de déballage supplémentaires. Réduisez la fréquence d’utilisation des appels de méthode d’interface car cela empêche l’inline.
Surtout dans les boucles car cela s'avère trop coûteux. Le déréférencement est quelque chose que nous ne voulons pas faire à nos propres frais.
La synchronisation des canaux est plus lente que les autres méthodes primitives de synchronisation. De plus, plus il y a de cas sélectionnés, plus notre programme sera lent. Cependant, select, case et default ont été optimisés.
Cela coûte également cher et nous devrions l'éviter. Par exemple, vérifiez (obtenez) l’index de tranche maximum une seule fois, et non plusieurs fois. Il est préférable d'essayer d'opter pour l'option extrême maintenant.
Tout au long de cet article, nous avons vu certaines des mêmes règles d'optimisation.
Aidez le compilateur à prendre la bonne décision et il vous remerciera. Allouez de la mémoire au moment de la compilation, utilisez des résultats intermédiaires et essayez de garder votre code lisible.
Je réitère que pour une optimisation implicite, les benchmarks sont obligatoires. Si quelque chose qui a fonctionné hier ne fonctionnera pas demain car le compilateur change trop rapidement entre les versions, et vice versa.
N'oubliez pas d'utiliser les outils d'analyse et de suivi intégrés de Go.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!