[LINUX] J'ai entendu des rumeurs selon lesquelles malloc est lent et devrait être stocké en mémoire, alors je l'ai comparé.

** Introduction **

J'ai entendu quelqu'un dire que "malloc est lent, il est donc plus rapide de stocker la mémoire et de la réutiliser". Si vous voulez vraiment le faire, vous devez le verrouiller, n'est-ce pas? En réfléchissant, il semble préférable de faire un rappeur si c'est vrai. Ce genre de chose ne peut pas être aidé même si vous continuez la théorie sur le bureau, alors mesurons-la! ** **

Cette fois, nous effectuerons des tests répétés de calloc / free pour les 4 types et mesures suivants **. J'ai choisi calloc car je l'utilise personnellement plus souvent. Je regrette que malloc soit plus purement précis

  1. Calloc ordinaire / gratuit
  2. Votre propre emballage
  3. Une version mise à jour qui élimine le goulot d'étranglement ci-dessus en empruntant la sagesse de nos prédécesseurs via Google Teacher
  4. Utilisez tc_malloc inclus dans les gperftools de google (cette méthode n'écrase pas de force le malloc normal)

La bibliothèque détermine simplement la quantité et la taille de la mémoire à stocker. Ainsi, en tant que prédiction avant la mesure, si elle se situe dans l'utilisation et la taille de la mémoire, 2 et 3 sont rapides, et en dehors de cette plage, 1 est une bonne correspondance, 4 ne l'est pas. Ce qui va se passer maintenant

** Spécifications de l'emballage **

Faites appeler la fonction init et créez-y une liste de mémoire. La liste a un indicateur utilisé / inutilisé, passant la mémoire inutilisée pendant l'appel et déplaçant ces données à la fin de la liste (queue). De cette façon, la première tête est toujours inutilisée et la queue utilise des données de mémoire. 初期構想.png

Au contraire, lorsqu'elles sont libres, les données de mémoire utilisées sont recherchées à partir de tail pour voir si elles correspondent aux données pour être libres. Les correspondances ne sont pas libérées, elles sont changées en inutilisées et déplacées au début. 初期構想_free.png

Comme on se souvient des positions de la tête et de la queue, je suis inquiet de chercher quand je suis libre, mais à part ça, je peux passer la mémoire immédiatement. N'est-ce pas un bon sentiment! ??

** Spécifications mises à jour qui éliminent les goulots d'étranglement **

2018/08/05 Addendum J'en ai fait une bibliothèque. Ce mempool.

Le cou de l'emballage ci-dessus est toujours le comportement lorsqu'il est libre. Lorsque presque toutes les données de la mémoire sont utilisées, presque toutes les données doivent être recherchées. Je me demandais si je devais revoir la structure de la liste de recherche, mais j'ai trouvé une bonne décision. Ce site, ma main est présentée comme une mauvaise réponse. Lol Si vous définissez ici la condition de la dernière réponse correcte, "** La taille de la zone mémoire est la même et il s'agit d'un octet carré de 2. **", la position peut être saisie immédiatement par calcul de bits. C'est merveilleux!

Sur la base de ce qui précède, nous avons examiné le wrapper qui devient le goulot d'étranglement.

Tout d'abord, au début. La liste et l'entité mémoire seront alignées. De plus, la spécification de taille est limitée à 2 octets. Ensuite, les entités de mémoire sont alignées en multiples de 2 ^ x comme ceci, donc si vous décalez les bits x fois quand ils sont libres, vous pouvez trouver la position. hhs_init.png

Maintenant, que se passe-t-il lorsque vous êtes libre? Tout d'abord, vous pouvez dire à partir de la plage d'adresses de l'entité mémoire créée en premier si l'entité mémoire à libérer est sous gestion. Vous n'êtes donc pas obligé de faire une recherche complète à chaque fois comme le premier wrapper. Également, le cas échéant, décalage de bits pour spécifier la position ⇒ Sortez l'entité de données de liste correspondante et déplacez-la vers la tête. Le processus de recherche n'est plus nécessaire. N'est-ce pas plus rapide?

hhs_free.png

De plus, je me suis référé à Code pour trouver cette position de bit pour savoir comment obtenir le décalage de bits x-time. Comme il n'est utilisé qu'au moment de l'initialisation, il n'est pas impliqué dans cette mesure, mais il est également étonnant que je n'ai pas besoin de temps pour travailler dur sur le changement de bits jusqu'à la fin.

Je pensais que cette zone était ma limite, je vais donc la mesurer.

** La mesure **

L'outil de mesure et le code source se trouvent ci-dessous. https://github.com/developer-kikikaikai/speedtest

Après la construction, vous pouvez mesurer en exécutant run.rb dans sample / mallocspeed.

** Conditions de mesure 1. **

Demandez à la bibliothèque de création de stocker 1024 1024 octets de mémoire. Dans ces conditions, les mesures suivantes sont effectuées 50 fois, 100 fois, 1024 fois et 10000 fois 10 fois, et la durée totale est sortie.

  1. 1024 octets malloc / gratuit
  2. 1024 octets * 2 et 1024 octets malloc / gratuit

La mesure du temps est effectuée dans la bibliothèque speedtest. (Étant donné que le journal chronométré est vidé dans la mémoire et affiché à la fin, je pense que la surcharge due à la mesure n'est pas tellement importante.

** Environnement de mesure **

Linux Ubuntu 18.04 Mémoire 8 Go 4 processeurs (sur VM)

** Comment lire les résultats **

La description sens
Colonne d'action Quelle mesure est représentée
Colonne de temps total temps total.-3 vaut 0.001(1/10^3)veux dire
XXX calloc(calloc/free under initial size) 1024 octets malloc/mesure gratuite
XXX calloc(calloc/free initial size+over size) 1024byte*Malloc 2 et 1024 octets/mesure gratuite
XXX=normal Calloc ordinaire/free(Décrit comme normal ci-dessous)
XXX=wrap Premier emballage
XXX=hhs_calloc Version d'élimination des goulots d'étranglement(En dessous de hhs_Décrit comme calloc)
XXX=tc_calloc tc_calloc/tc_Utilisez gratuitement(Ci-dessous tc_Décrit comme calloc)

** Résultat de la mesure **

** 50 fois x 10 mesures **

Action Total time
normal calloc(calloc/free under initial size) 0.15806e-3
wrap calloc code(calloc/free under initial size) 0.75032e-4
hhs_calloc(calloc/free under initial size) 0.135938e-3
tc_calloc(calloc/free under initial size) 0.186022e-3
normal calloc(calloc/free initial size+over size) 0.711725e-3
wrap calloc(calloc/free initial size+over size) 0.487427e-3
hhs_calloc(calloc/free initial size+over size) 0.443236e-3
tc_calloc(calloc/free initial size+over size) 0.702283e-3
  1. 1024 octets malloc / free: Premier wrapper >> hhs_calloc> Normal> tc_calloc
  2. 1024 octets * 2 et 1024 octets malloc / free: hhs_calloc c> Premier wrapper >> tc_calloc> Normal

Deux emballages sont rapides alors que le nombre est petit. Le premier emballage est particulièrement solide s'il n'est qu'à portée. De plus, si la taille est surdimensionnée, hhs_calloc est déjà bon. N'est-ce pas les deux bons?

(Les titres 1 et 2 sont omis ci-dessous)

** 100 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.283713e-3
wrap calloc code(calloc/free under initial size) 0.153599e-3
hhs_calloc(calloc/free under initial size) 0.272328e-3
tc_calloc(calloc/free under initial size) 0.293742e-3
normal calloc(calloc/free initial size+over size) 0.1394836e-2
wrap calloc(calloc/free initial size+over size) 0.1395083e-2
hhs_calloc(calloc/free initial size+over size) 0.1244906e-2
tc_calloc(calloc/free initial size+over size) 0.1337618e-2
  1. Premier wrapper> hhs_calloc> Normal> tc_calloc
  2. hhs_callo c> tc_calloc> premier wrapper> normal

C'est toujours environ 1/10 du nombre maximum d'enregistrements, mais la vitesse du premier wrapper a déjà fortement chuté ici. Cette zone est un niveau où le classement change à chaque fois qu'il est exécuté.

** 500 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.1453858e-2
wrap calloc code(calloc/free under initial size) 0.301273e-2
hhs_calloc(calloc/free under initial size) 0.1291004e-2
tc_calloc(calloc/free under initial size) 0.13424e-2
normal calloc(calloc/free initial size+over size) 0.7863012e-2
wrap calloc(calloc/free initial size+over size) 0.44953204e-1
hhs_calloc(calloc/free initial size+over size) 0.6339118e-2
tc_calloc(calloc/free initial size+over size) 0.6493924e-2
  1. hhs_calloc> = tc_calloc> Normal >>> Premier wrapper
  2. hhs_calloc> = tc_calloc> Normal >> Premier wrapper

Avec environ la moitié du nombre maximum d'enregistrements, le premier wrapper était de loin le plus lent. Abandonner plus vite que prévu (-_-;) De plus, la puissance de tc_calloc se démarque de ce domaine.

** 1024 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.2824874e-2
wrap calloc code(calloc/free under initial size) 0.20755398e-1
hhs_calloc(calloc/free under initial size) 0.2560653e-2
tc_calloc(calloc/free under initial size) 0.2751811e-2
normal calloc(calloc/free initial size+over size) 0.16774762e-1
wrap calloc(calloc/free initial size+over size) 0.54582233e-1
hhs_calloc(calloc/free initial size+over size) 0.13983322e-1
tc_calloc(calloc/free initial size+over size) 0.14046191e-1
  1. hhs_calloc> tc_calloc> Normal >>> Premier wrapper
  2. hhs_calloc> = tc_calloc> Normal >> Premier wrapper

Juste atteint le nombre maximum d'inscriptions. Hhhs_calloc fait de son mieux même dans le cas où, de manière inattendue, hors de portée est mélangé.

** 10000 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.30325826e-1
wrap calloc code(calloc/free under initial size) 0.5094687e-1
hhs_calloc(calloc/free under initial size) 0.30124209e-1
tc_calloc(calloc/free under initial size) 0.26917778e-1
normal calloc(calloc/free initial size+over size) 0.176232715e0
wrap calloc(calloc/free initial size+over size) 0.221244686e0
hhs_calloc(calloc/free initial size+over size) 0.176962985e0
tc_calloc(calloc/free initial size+over size) 0.146961605e0
  1. tc_calloc> hhs_calloc> = normal >>> premier wrapper
  2. tc_calloc> Normal> hhs_calloc >> Premier wrapper

Enfin tc_calloc est en haut. Il semble que plus le nombre est élevé, plus il est puissant. À ce stade, hhs_calloc n'est pas différent du calloc normal, donc je fais de mon mieux pour m'échapper avec un avantage allant jusqu'à 1024 fois.

** Résultat n ° 2: Résultat de l'écrasement de malloc par tc_malloc **

Ce résultat était intéressant, je vais donc le poster. C'est déjà une expérience de mesure de tc_malloc. Puisque chaque calloc / free est écrasé par tc_malloc, chaque résultat est plus long que le premier.

** 50 fois x 10 mesures **

Action Total time
normal calloc(calloc/free under initial size) 0.14662e-3
wrap calloc code(calloc/free under initial size) 0.7501e-4
hhs_calloc(calloc/free under initial size) 0.14337e-3
tc_calloc(calloc/free under initial size) 0.22985e-4
normal calloc(calloc/free initial size+over size) 0.803501e-3
wrap calloc(calloc/free initial size+over size) 0.28677e-3
hhs_calloc(calloc/free initial size+over size) 0.271435e-3
tc_calloc(calloc/free initial size+over size) 0.117837e-3
  1. tc_calloc> Premier wrapper >> hhs_calloc> Normal
  2. tc_calloc> hhs_calloc> premier wrapper> normal

Ici, tc_calloc est déjà de loin le meilleur. Je pensais que c'était à peu près la même chose que calloc / free, qui était complètement emballé, mais j'ai été surpris.

** 100 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.27454e-3
wrap calloc code(calloc/free under initial size) 0.147085e-3
hhs_calloc(calloc/free under initial size) 0.270945e-3
tc_calloc(calloc/free under initial size) 0.43941e-4
normal calloc(calloc/free initial size+over size) 0.1559897e-2
wrap calloc(calloc/free initial size+over size) 0.759443e-3
hhs_calloc(calloc/free initial size+over size) 0.489991e-3
tc_calloc(calloc/free initial size+over size) 0.255653e-3
  1. tc_calloc> premier wrapper> hhs_calloc> normal
  2. tc_calloc> hhs_calloc> premier wrapper> normal

La vitesse de tc_calloc est inchangée. A part ça, ça ne change pas. Les vitesses hors plage sont affectées par les écrasements calloc / free, et les wrappers sont plus rapides.

** 500 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.1373838e-2
wrap calloc code(calloc/free under initial size) 0.1687707e-2
hhs_calloc(calloc/free under initial size) 0.1296464e-2
tc_calloc(calloc/free under initial size) 0.262718e-3
normal calloc(calloc/free initial size+over size) 0.7076653e-2
wrap calloc(calloc/free initial size+over size) 0.13166146e-1
hhs_calloc(calloc/free initial size+over size) 0.2556278e-2
tc_calloc(calloc/free initial size+over size) 0.1392622e-2
  1. tc_calloc> hhs_calloc> normal> premier wrapper
  2. tc_calloc> hhs_calloc> normal> premier wrapper

Sans parler de tc_calloc. Compte tenu du premier résultat, hhs_calloc fait de son mieux.

** 1024 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.2763511e-2
wrap calloc code(calloc/free under initial size) 0.6518494e-2
hhs_calloc(calloc/free under initial size) 0.2707665e-2
tc_calloc(calloc/free under initial size) 0.561575e-3
normal calloc(calloc/free initial size+over size) 0.14081652e-1
wrap calloc(calloc/free initial size+over size) 0.16251563e-1
hhs_calloc(calloc/free initial size+over size) 0.4127922e-2
tc_calloc(calloc/free initial size+over size) 0.3438721e-2
  1. tc_calloc> hhs_calloc> normal> premier wrapper
  2. tc_calloc> hhs_calloc> normal> premier wrapper

On dit que la différence s'élargira dans la fourchette dans laquelle le rappeur devrait être bon. Je pense que tc_calloc utilise également bien la mémoire.

** 10000 fois x 10 **

Action Total time
normal calloc(calloc/free under initial size) 0.27661637e-1
wrap calloc code(calloc/free under initial size) 0.18210164e-1
hhs_calloc(calloc/free under initial size) 0.12308628e-1
tc_calloc(calloc/free under initial size) 0.9218671e-2
normal calloc(calloc/free initial size+over size) 0.148160442e0
wrap calloc(calloc/free initial size+over size) 0.84871154e-1
hhs_calloc(calloc/free initial size+over size) 0.68569389e-1
tc_calloc(calloc/free initial size+over size) 0.65872532e-1
  1. tc_calloc> hhs_calloc> premier wrapper> normal
  2. tc_calloc> hhs_calloc> premier wrapper> normal

Il semble y avoir une bibliothèque plus rapide que le champ que j'ai préparé. L'oncle qui l'a créé est triste.

** Mesure 2 **

Ce qui précède était une simple comparaison, donc cette fois, nous allons construire un scénario basé sur le cas d'utilisation de l'application à laquelle nous pensons.

** Condition de mesure. **

L'application cible est le "multithreading de ligttpd". Donc, je vais mesurer avec cette hypothèse.

Alors

élément conditions
Pool de mémoire 10 clients ⇒ 4096 octets x 256 x 10 regroupés
cas de test 10 de tous les clients sont connectés. Après avoir alloué autant de mémoire, la libération est répétée. * Étant donné que le moment du retour de la réponse du client est différent, modifiez l'ordre de libération le cas échéant.
Supposé tous les clients connectés 2000 (10,000 semble prendre du temps alors arrêtez)

Dans ces conditions, chaque mesure est effectuée 5 fois et la durée totale est sortie.

** Résultat de mesure 2. **

Call 5 times, input is 512000

Action Total time
normal calloc(calloc/free under initial size) 0.2694234765e1
wrap calloc code(calloc/free under initial size) 0.27786253728e2
hhs_calloc(calloc/free under initial size) 0.673090478e0
tc_calloc(calloc/free under initial size) 0.617789121e0

hhs_calloc> = tc_calloc> Normal> Premier wrapper

Les deux premiers sont des niveaux où l'ordre change en fonction du moment d'exécution

** Résultat n ° 2: Résultat de l'écrasement de malloc par tc_malloc **

Call 5 times, input is 512000

Action Total time
normal calloc(calloc/free under initial size) 0.593853921e0
wrap calloc code(calloc/free under initial size) 0.4174470792e1
hhs_calloc(calloc/free under initial size) 0.6199698e0
tc_calloc(calloc/free under initial size) 0.592267608e0

tc_calloc> = normal> = hhs_calloc> premier wrapper

Les trois précédents étaient des niveaux où l'ordre était modifié à chaque fois qu'il était exécuté. Cependant, dans ce résultat, le calloc remplacé par tc_malloc a un effet sur hhs_calloc. Comparez à nouveau avec seulement la mesure tc_calloc de tc_malloc.

Action Total time
normal calloc(calloc/free under initial size) 0.2694234765e1
wrap calloc code(calloc/free under initial size) 0.27786253728e2
hhs_calloc(calloc/free under initial size) 0.673090478e0
tc_calloc(calloc/free under initial size) 0.592267608e0

hhs_calloc> tc_calloc> Normal> Premier wrapper

En regardant les choses de cette façon, hhs_calloc peut être intéressant s'il est bien réglé.

2018/06/10 J'étais curieux de connaître les commentaires, alors j'ai également essayé de ne pas verrouiller hhs_calloc. Comparez côte à côte avec les résultats de tc_calloc.

Action Total time
hhs_calloc(calloc/free under initial size) 0.589852316e0
tc_calloc(calloc/free under initial size) 0.592267608e0

Oh, le record change à chaque fois que vous mesurez, mais s'il n'y a pas de verrou, hhs_calloc peut gagner. C'est intéressant si vous utilisez correctement les threads!

** Conclusion **

――Il est vrai que ** malloc / free sera plus rapide si vous décidez comment l'utiliser et bien l'emballer en fonction de la situation **. Cependant, si vous ne considérez pas sérieusement la vitesse, elle ralentira.

Au fait, je n'ai pas vu l'utilisation de la mémoire. Si l'utilisation de la mémoire de tc_malloc est énorme, vous pouvez créer votre propre wrapper.

référence

Référence pour éliminer les goulots d'étranglement Comment réaliser un grand nombre d'ensembles haute vitesse et économes en mémoire en utilisant une grande zone d'alignement de mémoire

Référence de décalage de bits ■ [C #] Code "Amazing" pour trouver la position de bit debout la plus à droite

Mesure: pour étudier le traitement du rubis et la virgule flottante lors du total des résultats À propos des fractions, Float et BigDecimal par Ruby ... (pour les débutants)

Recommended Posts

J'ai entendu des rumeurs selon lesquelles malloc est lent et devrait être stocké en mémoire, alors je l'ai comparé.
la mémoire n'est pas libérée uniquement par plt.close (). Cela devrait être plt.clf () → plt.close ().
J'entends que les boucles Matlab et Python sont lentes, mais est-ce vrai?
Je pensais qu'il serait lent d'utiliser l'instruction for dans NumPy, mais ce n'était pas le cas.
Notez que je comprends l'algorithme des moindres carrés. Et je l'ai écrit en Python.