#文字列とは何ですか?
Go では、文字列は (空の場合もある) 不変のバイトのシーケンスです。私たちにとって、ここでのキーワードは不変です。バイトスライスは可変であるため、文字列と []byte の間の変換には通常、割り当てとコピーが必要であり、コストがかかります。 内部では、Go の文字列は (現時点では) 長さと文字列データへのポインタとして表されます。文字列常駐とは何ですか?
次のコードについて考えてみましょう:b := []byte("hello") s := string(b) t := string(b)
func pointer(s string) uintptr { p := unsafe.Pointer(&s) h := *(*reflect.StringHeader)(p) return h.Data }
手動による文字列の永続化
Go では文字列を手動で永続化できます。必要なのは、おそらく map[[]byte]string のようなものを使用して、バイト スライスを指定して再利用する既存の文字列を見つける方法です。ルックアップが成功した場合は既存の文字列が使用され、失敗した場合は将来の使用に備えて文字列を変換して保存します。 ここには問題が 1 つだけあります。[]byte をマップのキーとして使用することはできません。 長期にわたるコンパイラの最適化のおかげで、代わりに map[string]string を使用できるようになりました。ここでの最適化は、キーが変換されたバイトのスライスであるマップ操作が、検索中に使用される新しい文字列を実際に生成しないことです。m := make(map[string]string) b := []byte("hello") s := string(b) // 分配了 _ = m[string(b)] // 不分配!
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 }
新たな問題 (同時実行の症状)
この手動の滞留ルーチンは滞留の問題を呼び出しコードに押し込むことに注意してください。マップへの同時アクセスを管理する必要があり、マップ (およびその中のすべて) の有効期間を決定する必要があり、文字列が必要になるたびにマップ検索の追加コストを支払う必要があります。 これらの決定を呼び出しコードにプッシュすると、パフォーマンスが向上します。たとえば、json を map[string]interface{} にデコードするとします。 json デコーダは同時実行できない可能性があります。マップのライフサイクルは json デコーダーに関連付けることができます。また、このマップのキーは頻繁に繰り返される可能性が高く、これが文字列常駐にとって最良のケースであるため、追加のマップ検索コストにはそれだけの価値があります。ヘルパー パッケージ
これらの複雑な問題を考慮したくないが、多少のパフォーマンスの低下を許容し、そこに文字列を常駐させる場合は、役立つコードについては、github.com/josharian/intern にパッケージがあります。 その仕組みは、sync.Pool をひどく悪用しています。常駐マップを sync.Pool に保存し、必要に応じて取得します。 sync.Pool への同時アクセスは安全であるため、これにより同時アクセスの問題が非常にうまく解決されます。 sync.Pool 内のコンテンツは通常、最終的にガベージ コレクションされるため、主に有効期間の問題が解決されます。 (ライフタイムの管理に関する関連資料については、Go の問題 29696 を参照してください。) 推奨チュートリアル: "以上がGo 文字列解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。