Versuch und Irrtum zur Verbesserung der cgo-Speicherprofilerstellung durch Anfänger

Dieser Artikel ist der 12. Tagesartikel von freee Engineers Advent Calendar.

Hallo. Mein Name ist @taiyo und ich bin Ingenieurpraktikant mit 17 Absolventen von freee. Ich habe nur Skriptsprachen wie Ruby für Freee, PHP für Kreise und Python für Abschlussarbeiten angesprochen. Ich hatte die Möglichkeit, mich mit go in freee an der Entwicklung zu beteiligen, und fing an, sie zu berühren, und ich fing vor ungefähr 3 Monaten an, go zu mögen.

Ich bin noch ungefähr 1 Jahr und 3 Monate ein neuer Ingenieur, aber ich wurde durch die später beschriebenen Ereignisse ausgelöst. Als "** Ich möchte die Speicherprofilerstellung auf der C-Seite mit vorgefertigten Anwendungen, die cgo ** verwenden, vereinfachen" hat zugenommen.

Ich möchte über diese drei Punkte schreiben.

Ich habe einen Speicherverlust auf meinem Go API-Server

Dies ist der Anfang der Sache. Nach der Freigabe einer wesentlichen Änderung am Go-API-Server trat einige Tage später ein Speicherverlust auf. Ich wachte auf ...! !!

Wie bereits erwähnt, bin ich nie auf das Phänomen von Speicherverlusten gestoßen, indem ich nur Skriptsprachen berührt habe, und es war meine erste Erfahrung, daher war ich wirklich ungeduldig. Ich wusste nicht, was ich tun sollte, aber ich fuhr mit Ratschlägen fort, während ich warm war.

Es wurde festgestellt, dass dies die Ursache für Speicherverluste in Go und C ist

Zusammenfassend wurde die Ursache sowohl auf der Go-Seite als auch auf der C-Seite gefunden.

Erste Umfrage

Die Anwendung enthielt also Go's Standardbibliothek net / http / pprof Ich habe es verwendet, um die Speichernutzung zu überprüfen. Während meiner Recherche erinnere ich mich, dass ich einfach "Wow!" Gesagt habe, um "net / http / pprof" und die Größe von go, die als Standardbibliothek implementiert ist, zu vereinfachen.

Immerhin in der Untersuchung hier ** CString von cgo, das nicht das Ziel von GC of go ist Daten, von denen ich dachte, ich könnte sie veröffentlichen Es stellte sich heraus, dass eine der Ursachen darin bestand, dass sie nicht veröffentlicht wurde **.

Code zur Freigabe hinzufügen ↓,

defer C.free(unsafe.Pointer(released))

Sofort reparieren und loslassen! Dies sollte es beheben!

... dachte ich, aber es gab immer noch ein Speicherleck.

Zweite Umfrage

Ich habe mit pprof gesucht und konnte keine mögliche Ursache finden. Da diese Anwendung cgo verwendet, habe ich beschlossen, den C-Code zu untersuchen und den ** C-Implementierungsteil von Go zu trennen und valgrind von ** zu setzen Bei der Untersuchung fand ich einen Teil auf der C-Seite, bei dem es sich wahrscheinlich um einen Leckagengpass handelte.

Ich habe es repariert und wieder freigegeben, und dann ist das Leck nicht aufgetreten, sodass die Situation vorerst geregelt war.

Ich dachte so

Ich möchte in der Lage sein, gemeinsam Forschung und C-Forschung zu betreiben

In der ersten Umfrage ist net / http / pprof erstaunlich! !! Ich dachte, aber es gab eine unerwartete Falle. Das Profilierungsziel von "net / http / pprof" ist nur der von go zugewiesene Speicher. ** C scheint nicht auszugeben **.

Obwohl es ein wichtiger Wert in C- und C ++ - Speicherprofilern ist, scheint es, wie oben erwähnt, von Go und cgo (https://github.com/golang/go/issues/782) bisher nicht unterstützt zu werden. Ich musste ** C-Code getrennt von Go ** untersuchen.

Dies würde separate ** go- und C-Untersuchungen ** erfordern, was einige Zeit in Anspruch nehmen würde. Stattdessen möchten Sie auf einen Schlag einen Engpass finden.

Es wäre schön, den Speicherstatus auf der C-Seite mit net / http / pprof sehen zu können

Ich möchte auch eine Umgebung schaffen, in der es einfach ist zu untersuchen, wann dasselbe passiert. net / http / pprof Immerhin ist es praktisch, also wäre es schön, in der Lage zu sein, auszugeben, welche Zeile von C mit derselben Schnittstelle Speicher belegt.

Da es cgo gibt, sollte die Kompatibilität zwischen go und C hoch sein, also frage ich mich, ob ich etwas dagegen tun kann. Also habe ich beschlossen, verschiedene Dinge auszuprobieren.

Politik

Die folgenden drei sind Kandidaten.

  1. Richten Sie die Zuordnungen Go und C aus
  2. Erstellen Sie eine Bibliothek, die C von Go mit derselben Schnittstelle wie "net / http / pprof" profiliert
  3. Geben Sie Ihr Bestes mit valgrind (behalten Sie den Status quo bei)

Was 2 betrifft, dachte ich, dass es viel Zeit und Kraft kosten würde, also Dieses Mal konzentrierte ich mich darauf zu untersuchen, ob die Profilerstellung von pprof durch "Ausrichten von Go- und C-Allokatoren" in 1 durchgeführt werden kann.

Hypothese zur Ausrichtung von Go- und C-Allokatoren

Vorerst folgte ich dem Quellcode von net / http / pprof. Schließlich kamen wir zu runtime / malloc.go

runtime/malloc.go


// Memory allocator.
//
// This was originally based on tcmalloc, but has diverged quite a bit.
// http://goog-perftools.sourceforge.net/doc/tcmalloc.html

// The main allocator works in runs of pages.
// Small allocation sizes (up to and including 32 kB) are
// rounded to one of about 70 size classes, each of which
// has its own free set of objects of exactly that size.
// Any free page of memory can be split into a set of objects
// of one size class, which are then managed using a free bitmap.
//
// The allocator's data structures are:
//
//	fixalloc: a free-list allocator for fixed-size off-heap objects,
//		used to manage storage used by the allocator.
//	mheap: the malloc heap, managed at page (8192-byte) granularity.
//	mspan: a run of pages managed by the mheap.
//	mcentral: collects all spans of a given size class.
//	mcache: a per-P cache of mspans with free space.
//	mstats: allocation statistics.

Wie Sie in diesem Kommentar sehen können, basiert ** go seine Speicherzuordnung auf tcmalloc, was in C normal ist. Ich habe Malloc benutzt. ** **. Wenn sich die für die Speicherzuweisung verwendeten Allokatoren unterscheiden und gleich sind, werden sie daher gut aufgenommen. Ich habe die Hypothese aufgestellt.

Eigentlich ursprünglich "Wie wäre es mit der Kombination der verschiedenen Allokatoren zwischen C und Go?" Ich wurde geraten. Zu dieser Zeit verstand ich es jedoch ehrlich gesagt nicht sehr gut und nachdem ich den Code bisher gelesen hatte, fühlte ich mich endlich verbunden. Testen wir vorerst die Hypothese, dass "versucht wird, den C-Allokator mit dem tcmalloc abzugleichen".

Ersetzt durch Gperftools

Der Ersatz durch malloc-> tcmalloc wird am häufigsten bei Suchanfragen gefunden und in der Hoffnung, dass er mit go von google gperftools kompatibel ist. Ich versuchte es. (Ich war ein wenig besorgt, dass es keinen brandneuen Artikel über gperftools gab, aber das Github-Repository-Commit war das neueste in 2016/11.)

Dieses Mal habe ich gperftools in die Version der go-Anwendung eingeführt, die den Speicherverlust verursacht hat, und den Vorgang überprüft. Die Entwicklungsumgebung wird mithilfe von Docker auf dem Container erstellt.

Unten ist der zusätzliche Code. (Vereinfacht)

#Dockerfile

...
RUN apt-get -y install google-perftools libgoogle-perftools-dev
...

cgo_sample.go


package main

/*
...
#cgo CFLAGS: -I/usr/include
#cgo LDFLAGS: -L/usr/lib -ltcmalloc
#include "gperftools/tcmalloc.h"
*/
import "C"
import (
  "net/http"
  _ "net/http/pprof"
);

func main() {
  http.HandleFunc("/", handler)
  http.ListenAndServe(":8800", nil)
}

func handler(w http.ResponseWriter, r *http.Request) {
  //Verarbeitung, die in C usw. ausläuft
}

Installieren Sie google-perftools und libgoogole-perftools-dev mit apt-get. Die beiden von apt-get installierten befinden sich unter / usr. Geben Sie sie daher in CFLAGS bzw. LDFLAGS an.

Prüfergebnis

$ curl http://localhost:8800
$ go tool pprof http://localhost:8800/debug/pprof/heap?debug=1
(pprof) text
5836.79kB of 5836.79kB total (  100%)
Dropped 57 nodes (cum <= 29.18kB)
Showing top 10 nodes out of 23 (cum >= 512.69kB)
      flat  flat%   sum%        cum   cum%
 2430.03kB 41.63% 41.63%  2430.03kB 41.63%  go.SomeLeakMethod1
 1334.04kB 22.86% 64.49%  1334.04kB 22.86%  go.OtherLeakMethod1
 1048.02kB 17.96% 82.44%  1048.02kB 17.96%  go.SomeLeakMethod2
  512.02kB  8.77%   100%   512.02kB  8.77%  go.OtherLeakMethod2
         0     0%   100%  1048.02kB 17.96%  go.NLeakMethod
         0     0%   100%  3764.07kB 64.49%  go.NLeakMethod2

Selbst wenn der Allokator auf tcmallocc ausgerichtet war, wurde nur die Go-Funktion angezeigt, die der Eingang zum Go war, und das Ergebnis war das gleiche wie das ursprüngliche pprof. Leider wird die Hypothese, dass "das Ausrichten der Allokatoren zur Ausgabe durch pprof führt", zurückgewiesen.

Hinweis

Es ist auch im ursprünglichen Repository von gperftools geschrieben, aber es scheint, dass gperftools unter Linux-64bit nicht gut funktioniert. Seien Sie also auch dort vorsichtig. Die Antwort war ungefähr 1,5-mal langsamer als gewöhnlich, obwohl es sich um eine Bibliothek zur Beschleunigung der Speicherzuweisung handeln sollte. .. Selbst wenn es gelingt, scheint die Installation ein wenig zu kosten.

Warum war es nicht gut?

Ersteres war das, was ich beim Lesen des Codes von malloc.go dachte. Letzteres könnte durch die Untersuchung von Prozessen und Threads entstanden sein.

In jedem Fall gab es Fortschritte bei der Verbesserung der Profilerstellung von cgo mit nur einer Möglichkeit.

Was ich darüber nachgedacht habe, mich zu verbessern

Geeignete Kenntnisse sind erforderlich, um Bereiche mit niedrigen Schichten zu verbessern

Durch die Speicherverlustreaktion meiner ersten Erfahrung wurde klar, dass nicht nur die Sprachspezifikationen von go, sondern auch Informatikkenntnisse weit verbreitet sind, um mit solchen Situationen umzugehen. Dieses Mal, insbesondere für Computer, konnte ich CPU und Speicher eingeben, und für Sprachen konnte ich Sprachspezifikationen für deren Verwendung eingeben, aber gleichzeitig genoss ich die praktische Oberfläche, die von meinen Vorgängern so wie sie war erstellt wurde. Ich verstand auch, dass ich es benutzte, ohne tief nachzudenken. Es macht mir klar, dass es noch viele Dinge gibt, die ich nicht weiß, und ich fühle mich angespannt.

Müssen auf eine gute Weise scheitern

In der Entwicklungskultur von Freee gibt es einen Punkt "** Lass uns scheitern und angreifen **". Ich erkenne dies als "ein Fehler ist eine Wachstumsquelle für mich und ich ergreife aggressive Maßnahmen, um denselben Fehler beim nächsten Mal zu verhindern."

Für mich ist genau das, was meiner Meinung nach an diesem Speicherverlust beteiligt ist.

  1. Was kann ich tun, um zu verhindern, dass in Zukunft Speicherlecks auftreten?
  2. Wenn Sie eine Situation schaffen können, in der die Speicherprofilerstellung reibungsloser durchgeführt werden kann, sollten Sie in der Lage sein, sie proaktiv auszuführen und zu verhindern.
  3. Lass es uns versuchen

Wenn Sie ein Gefühl der Herausforderung haben, werden Sie sich motivierter fühlen und es tun. Wir werden diese Denkweise nicht vergessen und unsere Stärke stärken, damit wir sicher Verbesserungsmaßnahmen präsentieren können.

Zusammenfassung

  1. ~~ Go- und C-Allokatoren ausrichten ~~
  2. Erstellen Sie eine Bibliothek, die C von Go mit derselben Schnittstelle wie "net / http / pprof" profiliert
  3. Geben Sie Ihr Bestes mit valgrind (behalten Sie den Status quo bei)

Ich habe diesmal 1 untersucht, aber leider keine guten Ergebnisse erzielt. Es war jedoch eine großartige Gelegenheit, den Quellcode zu lesen und zu verschieben, obwohl ich mit Go und C nicht vertraut war. Der Rest ist 2 (1) Plan, aber ich werde ein wenig Zeit damit verbringen, den Quellcode von Go zu lesen und an 2 zu arbeiten. Ich möchte den Follow-up-Bericht später aktualisieren.

Es ist das Ende, freee Co., Ltd. sucht langjähriger Ingenieurpraktikant, Ingenieur 17, 18 Absolventen, der Benutzern mit neuen Sprachen und Technologien wie Go einen ernsthaften Mehrwert bieten möchte. Ich bin. Es gibt auch ein Mittagessensystem, in dem Sie ins Büro kommen und zu Mittag essen können. Wenn Sie sich sowohl für Studenten als auch für Berufstätige interessieren, können Sie sich gerne an uns wenden.

Morgen, Dreieinhalb Jahre von der Entwicklung entfernt. Mein Lieblingsessen ist Komiyas Holzkohle. Dies ist unser CEO @ dice-sasaki @ github, der dieses Jahr wieder zu uns kommt! freue mich auf!

Recommended Posts

Versuch und Irrtum zur Verbesserung der cgo-Speicherprofilerstellung durch Anfänger
Versuch und Irrtum, um die Erzeugung von Wärmekarten zu beschleunigen
Versuch und Irrtum, um Android-Screenshots zu beschleunigen