[LINUX] Ich habe Gerüchte gehört, dass Malloc langsam ist und im Speicher gespeichert werden sollte, also habe ich es verglichen.

** Einführung **

Ich habe von jemandem gehört, dass "malloc langsam ist, daher ist es schneller, Speicher zu speichern und wiederzuverwenden." Wenn du es wirklich tun willst, musst du es sperren, nicht wahr? Während des Denkens scheint es besser, einen Rapper zu machen, wenn es wahr ist. So etwas kann nicht geholfen werden, selbst wenn Sie die Theorie auf dem Schreibtisch fortsetzen. Lassen Sie uns sie also tatsächlich messen! ** ** **

Dieses Mal ** werde ich einen wiederholten Test von calloc / free für die folgenden 4 Typen und Maßnahmen durchführen **. Ich habe mich für Calloc entschieden, weil ich es persönlich öfter benutze. Ich bedauere, dass Malloc rein genauer ist

  1. Gewöhnlicher Anruf / frei
  2. Ihr eigener Wrapper
  3. Eine aktualisierte Version, die den oben genannten Engpass beseitigt, indem sie die Weisheit unserer Vorgänger über Google Teacher ausleiht
  4. Verwenden Sie tc_malloc, das in den Gperftools von Google enthalten ist (diese Methode, um normale Malloc nicht zwangsweise zu überschreiben.)

Die Bibliothek bestimmt einfach die Größe und Größe des zu speichernden Speichers. Als Vorhersage vor der Messung sind 2 und 3 schnell, wenn sie innerhalb der Speichernutzung und -größe liegen, und außerhalb dieses Bereichs ist 1 eine gute Übereinstimmung, 4 nicht. Was wird jetzt passieren

** Wrapper-Spezifikationen **

Lassen Sie die Init-Funktion aufrufen und erstellen Sie dort eine Speicherliste. Die Liste verfügt über ein verwendetes / nicht verwendetes Flag, das während des Aufrufs nicht verwendeten Speicher weiterleitet und diese Daten an das Ende der Liste (Ende) verschiebt. Auf diese Weise wird der erste Kopf immer nicht verwendet und der Schwanz verwendet Speicherdaten. 初期構想.png

Im Gegensatz dazu werden die verwendeten Speicherdaten, wenn sie frei sind, vom Ende aus durchsucht, um festzustellen, ob sie mit den frei verfügbaren Daten übereinstimmen. Übereinstimmungen werden nicht freigegeben, sondern in nicht verwendete geändert und an den Anfang verschoben. 初期構想_free.png

Da die Positionen von Kopf und Schwanz in Erinnerung bleiben, mache ich mir Sorgen um die Suche, wenn ich frei bin, aber ansonsten kann ich die Erinnerung sofort weitergeben. Ist es nicht ein gutes Gefühl! ??

** Aktualisierte Spezifikationen zur Beseitigung von Engpässen **

Nachtrag 2018/08 Ich habe es zu einer Bibliothek gemacht. Dieser Mempool.

Der Hals der obigen Hülle ist immer noch das Verhalten, wenn er frei ist. Wenn fast alle Speicherdaten verwendet werden, müssen fast alle Daten durchsucht werden. Ich habe mich gefragt, ob ich die Struktur der Suchliste überprüfen muss, aber ich habe einen guten Schritt gefunden. Diese Seite, meine Hand wird als schlechte Antwort eingeführt. Lol Wenn Sie hier die Bedingung für die letzte richtige Antwort festlegen: "** Die Größe des Speicherbereichs ist gleich und es ist ein Quadratbyte von 2. **", kann die Position sofort durch Bitberechnung erfasst werden. Es ist wundervoll!

Basierend auf dem oben Gesagten haben wir den Wrapper überprüft, der zum Engpass wird.

Zunächst am Anfang. Die Liste und die Speicherentität werden in einer Reihe angeordnet. Darüber hinaus ist die Größenangabe auf 2 Bytes begrenzt. Dann werden die Speicherentitäten wie folgt in Vielfachen von 2 ^ x aneinandergereiht. Wenn Sie also die Bits x-mal verschieben, wenn sie frei sind, können Sie die Position finden. hhs_init.png

Was passiert nun, wenn Sie frei sind? Zunächst können Sie anhand des Adressbereichs der zuerst erstellten Speicherentität erkennen, ob sich die frei zu speichernde Speicherentität in der Verwaltung befindet. Sie müssen also nicht jedes Mal wie beim ersten Wrapper eine vollständige Suche durchführen. Gegebenenfalls auch Bitverschiebung, um die Position anzugeben. ⇒ Nehmen Sie die entsprechende Listendatenentität heraus und verschieben Sie sie in den Kopf. Der Suchvorgang wird nicht mehr benötigt. Ist das nicht schneller?

hhs_free.png

Außerdem habe ich auf Code zum Finden dieser Bitposition verwiesen, um die x-Zeit-Bitverschiebung zu erhalten. Da es nur zum Zeitpunkt der Initialisierung verwendet wird, ist es nicht an dieser Messung beteiligt, aber es ist auch erstaunlich, dass ich keine Zeit brauche, um hart an der Bitverschiebung bis zum Ende zu arbeiten.

Ich dachte, dass dieser Bereich meine Grenze ist, also werde ich ihn messen.

** Messung **

Das Messwerkzeug und der Quellcode befinden sich unten. https://github.com/developer-kikikaikai/speedtest

Nach dem Erstellen können Sie messen, indem Sie run.rb in sample / mallocspeed ausführen.

** Messbedingungen 1. **

Bitten Sie die Erstellungsbibliothek, 1024 bis 1024 Byte Speicher zu speichern. Unter diesen Bedingungen werden die folgenden Messungen 50 Mal, 100 Mal, 1024 Mal und 10000 Mal 10 Mal durchgeführt, und die Gesamtzeit wird ausgegeben.

  1. 1024 Byte Malloc / frei
  2. 1024 Byte * 2 und 1024 Byte Malloc / frei

Die Zeitmessung erfolgt in der Speedtest-Bibliothek. (Da das zeitgesteuerte Protokoll im Speicher abgelegt und am Ende angezeigt wird, ist der Overhead aufgrund der Messung meiner Meinung nach nicht so hoch.

** Messumgebung **

Linux Ubuntu 18.04 Speicher 8 GByte 4 CPUs (auf VM)

** Wie man die Ergebnisse liest **

Beschreibung Bedeutung
Aktionsspalte Welche Messung ist dargestellt
Gesamtzeitspalte Gesamtzeit.-3 ist 0.001(1/10^3)meint
XXX calloc(calloc/free under initial size) 1024 Byte Malloc/freie Messung
XXX calloc(calloc/free initial size+over size) 1024byte*2 und 1024 Byte Malloc/freie Messung
XXX=normal Gewöhnlicher Calloc/free(Wird unten wie gewohnt beschrieben)
XXX=wrap Erster Wrapper
XXX=hhs_calloc Version zur Beseitigung von Engpässen(Unten hhs_Beschrieben als Calloc)
XXX=tc_calloc tc_calloc/tc_Verwenden Sie kostenlos(Unten tc_Beschrieben als Calloc)

** Messergebnis **

** 50 mal x 10 Messungen **

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 Byte malloc / free: Erster Wrapper >> hhs_calloc> Normal> tc_calloc
  2. 1024 Byte * 2 und 1024 Byte malloc / free: hhs_calloc c> Erster Wrapper >> tc_calloc> Normal

Zwei Wrapper sind schnell, während die Anzahl klein ist. Der erste Wrapper ist besonders stark, wenn er sich nur in Reichweite befindet. Wenn die Größe übergroß ist, ist hhs_calloc bereits gut. Ist nicht beides gut?

(Titel 1 und 2 sind unten weggelassen)

** 100 mal 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. Erster Wrapper> hhs_calloc> Normal> tc_calloc
  2. hhs_callo c> tc_calloc> erster wrapper> normal

Es ist immer noch ungefähr 1/10 der maximalen Anzahl von Registrierungen, aber die Geschwindigkeit des ersten Wrappers ist hier bereits stark gesunken. Dieser Bereich ist eine Ebene, in der sich das Ranking bei jeder Ausführung ändert.

** 500 mal 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 >>> Erster Wrapper
  2. hhs_calloc> = tc_calloc> Normal >> Erster Wrapper

Mit etwa der Hälfte der maximalen Anzahl von Registrierungen war der erste Wrapper bei weitem der langsamste. Schneller aussteigen als ich erwartet hatte (-_-;) Auch die Leistung von tc_calloc hebt sich von diesem Bereich ab.

** 1024 mal 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 >>> Erster Wrapper
  2. hhs_calloc> = tc_calloc> Normal >> Erster Wrapper

Gerade die maximale Anzahl von Registrierungen erreicht. Hhhs_calloc gibt sein Bestes, selbst wenn unerwartet außerhalb des Bereichs gemischt wird.

** 10000 mal 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> = normaler >>> erster Wrapper
  2. tc_calloc> Normal> hhs_calloc >> Erster Wrapper

Schließlich ist tc_calloc oben. Es scheint, dass je mehr die Zahl, desto mächtiger sie ist. Zu diesem Zeitpunkt unterscheidet sich hhs_calloc nicht von normalem Calloc, daher versuche ich nur mein Bestes, um mit einem Vorteil von bis zu 1024 Mal zu entkommen.

** Ergebnis Nr. 2: Ergebnis des Überschreibens von Malloc durch tc_malloc **

Dieses Ergebnis war interessant, also werde ich es veröffentlichen. Es ist bereits ein Messexperiment von tc_malloc. Da jedes calloc / free von tc_malloc überschrieben wird, ist jedes Ergebnis länger als das erste.

** 50 mal x 10 Messungen **

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> Erster Wrapper >> hhs_calloc> Normal
  2. tc_calloc> hhs_calloc> erster Wrapper> normal

Hier ist tc_calloc schon bei weitem das Beste. Ich dachte, es wäre ungefähr das gleiche wie Calloc / Free, das komplett verpackt war, aber ich war überrascht.

** 100 mal 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> erster Wrapper> hhs_calloc> normal
  2. tc_calloc> hhs_calloc> erster Wrapper> normal

Die Geschwindigkeit von tc_calloc ist unvermindert. Davon abgesehen ändert sich nichts. Geschwindigkeiten außerhalb des Bereichs werden durch Calloc / Free-Overwrites beeinflusst, und Wrapper sind schneller.

** 500 mal 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> erster Wrapper
  2. tc_calloc> hhs_calloc> normal> erster Wrapper

Ganz zu schweigen von tc_calloc. In Anbetracht des ersten Ergebnisses gibt hhs_calloc sein Bestes.

** 1024 mal 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> erster Wrapper
  2. tc_calloc> hhs_calloc> normal> erster Wrapper

Es wird gesagt, dass sich der Unterschied innerhalb des Bereichs vergrößern wird, in dem der Rapper gut sein sollte. Ich denke, tc_calloc nutzt den Speicher auch gut.

** 10000 mal 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> erster Wrapper> normal
  2. tc_calloc> hhs_calloc> erster Wrapper> normal

Es scheint eine Bibliothek zu geben, die schneller ist als das Feld, das ich vorbereitet habe. Der Onkel, der es geschaffen hat, ist traurig.

** Messung 2 **

Das Obige war ein einfacher Vergleich, daher werden wir dieses Mal ein Szenario erstellen, das auf dem Anwendungsfall der Anwendung basiert, über die wir nachdenken.

** Messbedingung. **

Die Zielanwendung ist "Multithreading von ligttpd". Also werde ich mit dieser Annahme messen.

Damit

Element Bedingungen
Speicherpool 10 Clients ⇒ 4096 Bytes x 256 x 10 gepoolt
Testfall 10 aller Clients sind verbunden. Nachdem Sie so viel Speicher zugewiesen haben, wird die Freigabe wiederholt. * Da der Zeitpunkt für die Rückgabe der Antwort des Kunden unterschiedlich ist, ändern Sie die Freigabereihenfolge entsprechend.
Angenommen alle verbundenen Clients 2000 (10,000 scheint Zeit zu brauchen, also hör auf)

Unter diesen Bedingungen wird jede Messung fünfmal durchgeführt und die Gesamtzeit ausgegeben.

** Messergebnis 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> Erster Wrapper

Die ersten beiden Ebenen sind Ebenen, in denen sich die Reihenfolge abhängig vom Ausführungszeitpunkt ändert

** Ergebnis Nr. 2: Ergebnis des Überschreibens von Malloc durch 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> erster Wrapper

Die vorherigen drei Ebenen waren Ebenen, in denen die Reihenfolge bei jeder Ausführung geändert wurde. In diesem Ergebnis wirkt sich das durch tc_malloc ersetzte Calloc jedoch auf hhs_calloc aus. Vergleichen Sie noch einmal mit nur der tc_calloc-Messung von 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> Erster Wrapper

Wenn man es so betrachtet, kann hhs_calloc interessant sein, wenn es gut eingestellt ist.

2018/06/10 Ich war neugierig auf die Kommentare und habe auch versucht, hhs_calloc freizuschalten. Vergleichen Sie Seite an Seite mit den Ergebnissen von tc_calloc.

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

Oh, der Datensatz ändert sich jedes Mal, wenn Sie messen, aber wenn es keine Sperre gibt, kann hhs_calloc gewinnen. Es ist interessant, wenn Sie die Threads richtig verwenden!

** Fazit **

――Es ist wahr, dass ** malloc / free schneller ist, wenn Sie entscheiden, wie Sie es verwenden und es je nach Situation gut einwickeln **. Wenn Sie die Geschwindigkeit jedoch nicht ernsthaft berücksichtigen, wird sie langsamer.

Übrigens habe ich die Speichernutzung nicht gesehen. Wenn der Speicherbedarf von tc_malloc sehr hoch ist, möchten Sie möglicherweise Ihren eigenen Wrapper erstellen.

Referenz

Referenz zur Beseitigung von Engpässen Wie man eine große Anzahl von Hochgeschwindigkeits- und speichereffizienten Sätzen unter Verwendung eines großen Speicherausrichtungsbereichs realisiert

Bitverschiebungsreferenz ■ [C #] "Erstaunlicher" Code zum Finden der am weitesten rechts stehenden Bitposition

Messung: Zur Untersuchung der Rubinverarbeitung und des Gleitkommas bei der Summierung der Ergebnisse Über Brüche, Float und BigDecimal von Ruby ... (für Anfänger)

Recommended Posts

Ich habe Gerüchte gehört, dass Malloc langsam ist und im Speicher gespeichert werden sollte, also habe ich es verglichen.
Der Speicher wird nicht nur von plt.close () freigegeben. Es sollte plt.clf () → plt.close () sein.
Ich höre, dass Matlab- und Python-Schleifen langsam sind, aber ist das wahr?
Ich dachte, es wäre langsam, die for-Anweisung in NumPy zu verwenden, aber das war nicht der Fall.
Beachten Sie, dass ich den Algorithmus der kleinsten Quadrate verstehe. Und ich habe es in Python geschrieben.