Heim  >  Artikel  >  Backend-Entwicklung  >  Go: Einfache Optimierungshinweise

Go: Einfache Optimierungshinweise

Go语言进阶学习
Go语言进阶学习nach vorne
2023-07-21 13:04:42908Durchsuche
Im Zeitalter des Cloud Computing erstellen wir häufig serverlose Anwendungen (ein Cloud-natives Entwicklungsmodell, das es Entwicklern ermöglicht, Anwendungen zu erstellen und auszuführen, ohne Server verwalten zu müssen). Wenn unsere Projekte dieses Modell übernehmen, steht das Budget für die Instandhaltung der Infrastruktur ganz oben auf der Liste. Wenn die Auslastung unseres Dienstes gering ist, ist er praktisch kostenlos. Aber wenn etwas schief geht, zahlen Sie viel dafür! Wenn es um Geld geht, muss man zwangsläufig in irgendeiner Weise darauf reagieren.

Wenn Ihr VPS mehrere Dienstanwendungen ausführt, eine davon jedoch manchmal alle Ressourcen beansprucht, sodass kein Zugriff auf den Server über SSH möglich ist. Sie wechseln zur Verwendung eines Kubernetes-Clusters und legen Grenzwerte für alle Anwendungen fest. Wir sahen dann, wie einige Anwendungen neu gestartet wurden, da der OOM-Killer das Problem des Speicherverlusts behoben hatte.

Natürlich ist OOM nicht immer ein Leckproblem, es kann auch eine Ressourcenüberschreitung sein. Leckageprobleme werden höchstwahrscheinlich durch Programmfehler verursacht. Das Thema, über das wir heute sprechen, ist, wie diese Situation so weit wie möglich vermieden werden kann.

Übermäßiger Ressourcenverbrauch schadet dem Geldbeutel, weshalb wir sofort handeln müssen.

Optimieren Sie nicht voreilig

Jetzt reden wir über Optimierung. Hoffentlich können Sie verstehen, warum wir nicht voreilig optimieren sollten!

  • Erstens kann die Optimierung nutzlose Arbeit sein. Weil wir zuerst die gesamte Anwendung untersuchen sollten und Ihr Code höchstwahrscheinlich nicht der Engpass sein wird. Was wir brauchen, sind schnelle Ergebnisse, MVP (Minimum Viable Product, minimal lebensfähiges Produkt), und dann werden wir uns mit seinen Problemen befassen.
  • Zweitens muss die Optimierung eine Grundlage haben. Das heißt, jede Optimierung sollte auf einem Benchmark basieren und wir müssen nachweisen, wie viel Gewinn sie uns bringt.
  • Drittens kann Optimierung Komplexität mit sich bringen. Sie müssen wissen, dass die meisten Optimierungen die Lesbarkeit Ihres Codes beeinträchtigen. Sie müssen dieses Gleichgewicht finden.

Optimierungsvorschläge

Jetzt geben wir einige praktische Vorschläge gemäß der Standard-Entitätsklassifizierung in Go.

1. Arrays und Slices

Speichern Sie Slices im Voraus zu.

Versuchen Sie, den dritten Parameter zu verwenden: <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)

Wenn die genaue Anzahl der Elemente nicht bekannt ist und Das Slice ist von kurzer Dauer. Ja, Sie können eine größere Größe zuweisen, um sicherzustellen, dass das Slice während der Laufzeit nicht wächst.

Vergessen Sie nicht, das Kopieren zu verwenden.

Versuchen Sie, beim Kopieren, z. B. beim Zusammenführen von zwei oder mehr Slices, nicht das Anhängen zu verwenden.

Korrekte Iteration

Ein Slice mit vielen Elementen oder großen Elementen, verwenden Sie for, um ein einzelnes Element zu erhalten. Auf diese Weise werden unnötige Doppelarbeit vermieden.

Multiplexen von Slices

Wenn eine Operation am eingehenden Slice ausgeführt wird und ein geändertes Ergebnis zurückgibt, können wir es zurückgeben. Dadurch wird eine neue Speicherzuweisung vermieden.

🎜
Lassen Sie keinen ungenutzten Teil der Scheibe übrig.

Wenn Sie ein kleines Stück der Scheibe abschneiden und nur verwenden müssen, bleibt auch der Hauptteil der Scheibe erhalten. Der richtige Ansatz besteht darin, eine neue Kopie dieses kleinen Slice zu verwenden und den alten Slice an den GC zu übergeben.

2. Korrektes Zusammenfügen von Zeichenfolgen

Wenn das Zusammenfügen von Zeichenfolgen in einer Anweisung erfolgen kann, verwenden Sie <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>+ Operator. Wenn Sie dies in einer Schleife tun müssen, verwenden Sie

🎜string.Builder🎜🎜 und verwenden Sie dessen 🎜🎜Grow🎜🎜 Methode ist vorab angegeben🎜🎜Builder🎜🎜 Größe, wodurch die Anzahl der Speicherzuweisungen reduziert wird. 🎜🎜🎜Konvertierungsoptimierung🎜🎜🎜String und []Byte sind in der zugrunde liegenden Struktur sehr ähnlich, und manchmal kann eine starke Konvertierung zwischen diesen beiden Typen verwendet werden, um eine Speicherzuweisung zu vermeiden. 🎜🎜🎜String-Resident🎜🎜🎜 kann Strings bündeln und so dem Compiler helfen, denselben String nur einmal zu speichern. 🎜🎜🎜Zuweisung vermeiden🎜🎜🎜Wir können Map (Kaskade) anstelle zusammengesetzter Schlüssel verwenden, wir können Byte-Slices verwenden. Versuchen Sie, 🎜🎜 fmt 🎜🎜 Paket, da alle seine Methoden Reflektion verwenden. 🎜🎜

3. Struktur

Vermeiden Sie das Kopieren großer Strukturen

Wir verstehen, dass kleine Strukturen nicht mehr als 4 Felder und nicht mehr als eine Maschinenwortgröße haben.

Einige typische Kopierszenarien

  • Projiziert auf die Schnittstelle
  • Kanalempfang und -sendung
  • Ersetzen von Elementen in der Karte
  • Elemente zu Slices hinzufügen
  • Iteration (Bereich)
Vermeiden Sie den Zugriff auf Strukturfelder über Zeiger

Dereferenzierung ist teuer und wir sollten es so wenig wie möglich tun, insbesondere in Schleifen. Außerdem geht die Möglichkeit verloren, schnelle Register zu verwenden.

Umgang mit kleinen Strukturen

Diese Arbeit ist vom Editor optimiert und daher günstig.

Verwenden Sie die Ausrichtung, um die Strukturgröße zu reduzieren

Wir können die Größe der Struktur selbst reduzieren, indem wir die Struktur ausrichten (in der richtigen Reihenfolge entsprechend der Größe der Felder anordnen).

4. Funktionen

Verwenden Sie Inline-Funktionen oder integrieren Sie sie selbst.

Versuchen Sie, kleine Funktionen zu schreiben, die vom Compiler eingebunden werden können. Dies geht schneller, sogar schneller, als den Code selbst in die Funktion einzubetten. Dies gilt insbesondere für heiße Pfade.

Welche werden nicht inline sein

  • Wiederherstellung
  • Block auswählen
  • Typdeklaration
  • aufschieben
  • goroutine
  • für den Bereich
vernünftig Wählen Sie Funktionsparameter mit Bedacht aus.

Versuchen Sie, kleine Parameter zu verwenden, da deren Replikation optimiert wird. Versuchen Sie, die Replikation und das Stapelwachstum bei der GC-Last im Gleichgewicht zu halten. Vermeiden Sie eine große Anzahl von Parametern und lassen Sie Ihr Programm schnelle Register verwenden (deren Anzahl ist begrenzt).

Benannte Rückgabewerte

Dies scheint effizienter zu sein als die Deklaration dieser Variablen im Funktionskörper.

Zwischenergebnisse speichern

Helfen Sie dem Compiler, Ihren Code zu optimieren, speichern Sie die Zwischenergebnisse, und dann stehen Ihnen weitere Optionen zur Optimierung Ihres Codes zur Verfügung.

Verwenden Sie die Verzögerung vorsichtig.

Versuchen Sie, die Verzögerung nicht zu verwenden, oder verwenden Sie sie zumindest nicht in einer Schleife.

Help Hot Path

Vermeiden Sie die Zuweisung von Speicher im Hot Path, insbesondere bei kurzlebigen Objekten. Machen Sie die häufigsten Zweige (wenn, wechseln)

5. Map

Speicher im Voraus zuweisen

Genau wie Slice, geben Sie beim Initialisieren der Map dessen Größe an.

Verwenden Sie leere Strukturen als Werte

struct{} ist nichts (beansprucht keinen Speicher), daher ist es sehr nützlich, es beispielsweise bei der Weitergabe von Signalen zu verwenden.

Karte löschen

Karte kann nur wachsen, nicht schrumpfen. Wenn wir die Karte zurücksetzen müssen, hilft das Löschen aller ihrer Elemente nicht.

Versuchen Sie, in Schlüsseln und Werten keine Zeiger zu verwenden.

Wenn die Karte keine Zeiger enthält, verschwendet der GC keine wertvolle Zeit damit. Da auch Strings Zeiger verwenden, sollten Sie Byte-Arrays anstelle von Strings als Schlüssel verwenden.

Reduzieren Sie die Anzahl der Änderungen

Auch hier möchten wir keine Zeiger verwenden, sondern können eine Kombination aus Karte und Slice verwenden und die Schlüssel in der Karte und die Werte im Slice speichern. Auf diese Weise können wir den Wert ohne Einschränkungen ändern. 6. Schnittstelle Der Schlüssel liegt im Kopieren. Es stellt sich heraus, dass die Kosten für das Ein- und Auspacken der Schnittstelle ungefähr den Kosten für die Zuweisung der Strukturgröße entsprechen.

Auswahl des optimalen Typs

In einigen Fällen erfolgt keine Zuordnung beim Ein- und Auspacken von Schnittstellen. Zum Beispiel kleine oder boolesche Werte für Variablen und Konstanten, Strukturen mit einem einfachen Feld, Zeiger (einschließlich Karte, Kanal, Funktion).

Vermeiden Sie Speicherzuweisungen.

Versuchen Sie wie anderswo auch, unnötige Zuweisungen zu vermeiden. Zum Beispiel eine Schnittstelle einer anderen zuweisen, anstatt zweimal zu boxen.

Nur bei Bedarf verwenden

Vermeiden Sie die Verwendung von Schnittstellen in häufig aufgerufenen Funktionsparametern und Rückgabeergebnissen. Wir benötigen keine zusätzlichen Auspackvorgänge. Reduzieren Sie die Häufigkeit der Verwendung von Schnittstellenmethodenaufrufen, da dadurch Inlining verhindert wird.

7. Pointer, Channels, Bounds Checks

Vermeiden Sie unnötige Dereferenzierungen

Besonders in Schleifen, da es sich als zu teuer herausstellt. Dereferenzierung ist etwas, was wir nicht auf eigene Kosten tun wollen.

Die Verwendung von Kanälen ist ineffizient.

Die Kanalsynchronisierung ist langsamer als andere primitive Synchronisierungsmethoden. Darüber hinaus gilt: Je mehr Fälle ausgewählt werden, desto langsamer wird unser Programm. Select, Case und Default wurden jedoch optimiert.

Vermeiden Sie unnötige Grenzkontrollen

Das ist auch teuer und wir sollten es vermeiden. Überprüfen (erhalten) Sie beispielsweise den maximalen Slice-Index nur einmal, nicht mehrmals. Versuchen Sie es am besten jetzt mit der extremen Option.

Zusammenfassung

In diesem Artikel haben wir einige der gleichen Optimierungsregeln gesehen.

Helfen Sie dem Compiler, die richtige Entscheidung zu treffen, und er wird es Ihnen danken. Weisen Sie zur Kompilierungszeit Speicher zu, verwenden Sie Zwischenergebnisse und versuchen Sie, Ihren Code lesbar zu halten.

Ich betone noch einmal, dass für die implizite Optimierung Benchmarks obligatorisch sind. Wenn etwas, das gestern funktioniert hat, morgen nicht funktioniert, weil der Compiler zu schnell zwischen den Versionen wechselt und umgekehrt.

Vergessen Sie nicht, die integrierten Analyse- und Tracking-Tools von Go zu verwenden.

Das obige ist der detaillierte Inhalt vonGo: Einfache Optimierungshinweise. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:Go语言进阶学习. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen