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
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
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.
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.
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! ??
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.
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?
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.
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.
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.
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.
Linux Ubuntu 18.04 Mémoire 8 Go 4 processeurs (sur VM)
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) |
** 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 |
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 |
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 |
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 |
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 |
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.
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 |
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 |
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 |
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 |
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 |
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.
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.
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.
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
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!
――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 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