Go1.10 strings.Builder の紹介

Go1.10 strings.Builder の紹介

Go 1.10 Release Party in Tokyo の発表です

Fd1ded499f7831ddb01b0d23eb9b64c2?s=128

Kenshi Kamata

February 20, 2018
Tweet

Transcript

  1. 2.

    自己紹介 • 鎌田 健史 • @knsh14 (twitter, GitHub) • KLab

    株式会社 ◦ Unity のエディタ拡張書いたり ◦ JavaScript でゲーム書いたり • 技術書典ではバイトしてました
  2. 6.

    strings.Builder を作った理由 • Issue ◦ https://github.com/golang/go/issues/18990 ◦ https://go-review.googlesource.com/c/go/+/74931 • bytes.Buffer

    に溜め込んだ文字列を string にする時に発生する余 計なアロケーションを回避したい
  3. 7.

    パフォーマンス見てみる https://gist.github.com/knsh14/fd4b0413a32b8b2ca544f2706d8557 d0 1. 100 回 WriteString してその結果を n 回取り出す

    2. n 回 WriteString してその結果を String() で取り出す 3. 事前に n * 10 で容量を確保しておき、2と同じことをする 4. これを 10, 100, 1000, 10000, 100000 回で試す
  4. 9.

    なぜだろう? • bytes.Buffer の String → メモリコピーが走る ◦ String(b []byte)している

    • strings.Builder の String → メモリコピーが走らない ◦ unsafe.Pointer からのキャスト • ちょうど修正したところが活かされている
  5. 10.

    もう一つのベンチマーク • n 回 WriteString して書き込んで一度だけ String() を読んで文字列 を取得すると若干遅くなる •

    特に allocation とメモリのパフォーマンスが落ちる • 事前に Grow(n int) している場合には遅くならない ◦ Grow は Buffer の cap を増やす処理 • 実装を調べてみる
  6. 13.

    実装の比較 • bytes.Buffer の grow は cap を増やす時に もとの長さ *

    2 + 増や す分 の長さを取っている ◦ https://golang.org/src/bytes/buffer.go?s=4258:4292#L144 • strings.Builder は append(d []byte, s string) • 繰り返しが小さいと差が出づらい ◦ 小さい要素だと append も倍増やす仕組みになっているらしい • この差で bytes.Buffer は少し make する回数が減っているのでは ないか?
  7. 14.

    どう使い分けるのがいいのか? • どれくらい buffer に書き込むか事前にわからない場合は bytes.Buffer のほうが少し有利 • String() を何回も呼ぶ必要がある場合には

    strings.Builder のほう が圧倒的に有利 • 事前にどれくらい書き込むかわかっている場合には strings.Builder がおすすめ
  8. 16.

    コピーされたかどうか検知する • https://play.golang.org/p/nzYsBpxYZpc • struct をコピーすると、フィールドがポインタの場合はアドレスがコ ピーされる ◦ フィールドのアドレスが共有され、思わぬ副作用が •

    これを回避したい ◦ 最初に書き込むときに自分自身のアドレスを持っておく ◦ B := A とコピーする ◦ B 自身のポインタとフィールドが持っているポインタが違うのでコ ピーされたことが検出できる
  9. 17.

    noescape を使ってパフォーマンス改善 • https://github.com/golang/go/issues/23382 • https://go-review.googlesource.com/c/go/+/86976 • コピーされてないかチェックするだけでメモリ確保が走ってしまうのを 防ぐ •

    `//go:nosplit` がついてる関数ではスタックの操作はしない ◦ Goならわかるシステムプログラミング Go言語のメモリ管理 を 参考にしました