Heim  >  Artikel  >  Backend-Entwicklung  >  Gehen Sie zum String-Parsen

Gehen Sie zum String-Parsen

Guanhui
Guanhuinach vorne
2020-06-12 18:21:233013Durchsuche

Gehen Sie zum String-Parsen

Was ist eine Zeichenfolge?

In Go ist ein String eine (möglicherweise leere) unveränderliche Folge von Bytes. Für uns ist das Schlüsselwort hier unveränderlich. Da Byte-Slices veränderbar sind, erfordert die Konvertierung zwischen String und []Byte normalerweise eine Zuweisung und ein Kopieren, was teuer ist.

Unter der Haube werden Gos Strings (derzeit) als Länge und Zeiger auf die String-Daten dargestellt

Was ist stringresident?

Bedenken Sie diesen Code:

b := []byte("hello")
s := string(b)
t := string(b)

s und t sind Zeichenfolgen, daher haben beide Längen- und Datenzeiger. Ihre Längen sind offensichtlich gleich. Was ist mit ihren Datenzeigern?

Die Go-Sprache kann uns keine direkte Suchmethode bieten. Aber wir können unsafe verwenden, um Folgendes zu prüfen:

func pointer(s string) uintptr {
    p := unsafe.Pointer(&s)
    h := *(*reflect.StringHeader)(p)
    return h.Data
}

(Diese Funktion sollte unsafe.Pointer zurückgeben. Weitere Informationen finden Sie im Go-Problem 19367.)

Wenn wir fmt.Println(pointer(s), pointer ( t)) erhalten wir Informationen ähnlich wie 4302664 4302632. Zeiger sind unterschiedlich; sie haben zwei separate Kopien der Daten.

(Dies ist ein Übungslink. Wenn Sie es ausprobieren möchten, was passiert, wenn Sie „Hallo“ in „h“ ändern? Erklärung)

Angenommen, Sie möchten die einzelnen Daten „Hallo“ wiederverwenden Kopie? Das ist String-Residency. Die String-Residenz hat zwei Vorteile. Der offensichtliche Vorteil besteht darin, dass Sie keine Daten zuweisen und kopieren müssen. Ein weiterer Vorteil besteht darin, dass die Überprüfung der Zeichenfolgengleichheit beschleunigt wird. Wenn zwei Strings die gleiche Länge und den gleichen Datenzeiger haben, müssen sie nicht auf die Bytes überprüft werden.

Ab Go 1.14 speichert Go die meisten Zeichenfolgen nicht mehr. Wie andere Formen des Cachings ist auch die Persistenz mit Kosten verbunden: Synchronisierung für Parallelitätssicherheit, Komplexität des Garbage Collectors und zusätzlicher Code, der jedes Mal ausgeführt werden muss, wenn eine Zeichenfolge erstellt wird. Und wie beim Caching gibt es Situationen, in denen es eher schädlich als hilfreich sein kann. Wenn Sie mit Wörtern in einem Wörterbuch arbeiten, in denen kein Wort zweimal vorkommt, verschwendet die Zeichenfolgenpersistenz Zeit und Speicher.

Manuelle Zeichenfolgenpersistenz

Es ist möglich, Zeichenfolgen in Go manuell beizubehalten. Was wir brauchen, ist eine Möglichkeit, einen vorhandenen String zu finden, der bei gegebenem Byte-Slice wiederverwendet werden kann, vielleicht mit etwas wie „map[[]byte]string“. Wenn die Suche erfolgreich ist, wird die vorhandene Zeichenfolge verwendet. Wenn sie fehlschlägt, konvertieren wir die Zeichenfolge und speichern sie für die zukünftige Verwendung.

Hier gibt es nur ein Problem: Sie können []Byte nicht als Schlüssel für eine Karte verwenden.

Dank langjähriger Compiler-Optimierungen können wir stattdessen „map[string]string“ verwenden. Eine Optimierung besteht darin, dass Kartenoperationen, deren Schlüssel Slices transformierter Bytes sind, keine neuen Zeichenfolgen generieren, die bei Suchvorgängen verwendet werden.

m := make(map[string]string)
b := []byte("hello")
s := string(b) // 分配了
_ = m[string(b)] // 不分配!

(Ähnliche Optimierungen gelten für andere Fälle, in denen der Compiler nachweisen kann, dass das konvertierte Byte-Slice während der Verwendung nicht geändert wird, z. B. Schalterzeichenfolge (b), wenn alle Schalter )

Alle Der zum Beibehalten der Zeichenfolge erforderliche Code lautet wie folgt:

func intern(m map[string]string, b []byte) string {
    // 查找一个存在的字符串来重用
    c, ok := m[string(b)]
    if ok {
        // 找到一个存在的字符串
        return c
    }
    // 没有找到,所以制作一个并且存储它
    s := string(b)
    m[s] = s
    return s
}

Es ist einfach

Neue Schwierigkeit (Parallelität) Symptome)

Beachten Sie, dass es sich um eine manuelle Verweilroutine handelt schiebt das Verweilproblem in den aufrufenden Code. Sie müssen den gleichzeitigen Zugriff auf die Karte verwalten; Sie müssen die Lebensdauer der Karte (und aller darin enthaltenen Elemente) bestimmen und jedes Mal, wenn Sie eine Zeichenfolge benötigen, die zusätzlichen Kosten für eine Kartensuche bezahlen.

Die Übertragung dieser Entscheidungen auf den aufrufenden Code führt zu einer besseren Leistung. Angenommen, Sie dekodieren JSON, um [string]interface{} abzubilden. Der JSON-Decoder ist möglicherweise nicht gleichzeitig. Der Lebenszyklus der Karte kann an den JSON-Decoder gebunden werden. Und die Schlüssel dieser Karte werden wahrscheinlich oft wiederholt, was für die String-Residency der beste Fall ist. Dadurch lohnen sich die zusätzlichen Kosten für die Kartensuche.

Ein Hilfspaket

Wenn Sie keine dieser Komplikationen in Betracht ziehen möchten und bereit sind, einen leichten Leistungseinbruch in Kauf zu nehmen, und die Saiten dort ansässig haben, könnte dies der Fall sein Für den hilfreichen Code gibt es ein Paket dafür: github.com/josharian/intern.

Wie es funktioniert, ist ein schrecklicher Missbrauch von sync.Pool. Es speichert residente Karten in sync.Pool und ruft sie bei Bedarf ab. Dies löst das Problem des gleichzeitigen Zugriffs sehr gut, da der Zugriff auf sync.Pool gleichzeitig sicher ist. Es löst hauptsächlich das Lebensdauerproblem, da die Inhalte in sync.Pool normalerweise irgendwann durch Müll gesammelt werden. (Weitere Informationen zum Verwalten von Lebenszeiten finden Sie im Go-Problem 29696.)

Empfohlene Tutorials: „PHP“ „GO-Tutorial

Das obige ist der detaillierte Inhalt vonGehen Sie zum String-Parsen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen