LoginSignup
154
115

More than 3 years have passed since last update.

golang で string を []byte にキャストするとメモリコピーが走ります

Last updated at Posted at 2014-09-03

いまはもっと賢くなってる。最適化バンザイ
https://medium.com/a-journey-with-go/go-string-conversion-optimization-767b019b75ef

概要

string を byte スライスにしたり,またその逆だったりをおこなうのに,

string2byteslice
vec := []byte("hello")

なんてコードをよく書きます.string は読み込み専用のスライスみたいな物だという認識だったので,キャストしても,ポインタがコピーされるだけで,必要になったらコピーされるだろうぐらいに思ってたんですが,調べてみたらメモリがまるっとコピーされるのでパフォーマンスに影響しそうなときは要注意です.

詳細

string を byte スライスにキャストするプログラムを書いて,アセンブリコードを吐かせてみました.

キャストするだけのプログラム
1  package main
2
3  import "fmt"
4
5  func main() {
6          str := "hello, world!"
7          vec := []byte(str)
8          fmt.Println(vec)
9  }

7行目でキャストしてます.下記の要領でアセンブリを吐き出します.

% go tool 8g -S stringbytes.go

なかみをのぞいてみると,7行目でキャストしているコードに対応するところで
runtime.stringtoslicebyte() というのが呼ばれています.

0x0039 00057 (stringbytes.go:7) CALL    ,runtime.stringtoslicebyte(SB)

探してみたところ,こいつは,src/pkg/runtime/string.goc にいました.

src/pkg/runtime/string.goc
func stringtoslicebyte(s String) (b Slice) {
        uintptr cap;

        cap = runtime·roundupsize(s.len);
        b.array = runtime·mallocgc(cap, 0, FlagNoScan|FlagNoZero);
        b.len = s.len;
        b.cap = cap;
        runtime·memmove(b.array, s.str, s.len);
        if(cap != b.len)
                runtime·memclr(b.array+b.len, cap-b.len);
}

どうやらメモリを確保してコピーしているようです.なるほど・・・.

感想

strings パッケージの ReadByte() とか使ったり,そもそも byte スライスで扱うように見直してみたりすることで,むやみにメモリコピー&GCが走るのを抑制できそうです.string をキャストする際には一呼吸置いて考える必要がありそうです.

追記:byte スライスから string へのキャスト

byte スライスから string へのキャストだと,もしかしたらメモリコピーしなくてもすむんじゃないかと思って調べてみた.

src/pkg/runtime/string.goc
func slicebytetostring(b Slice) (s String) {
        void *pc;

        if(raceenabled) {
                pc = runtime·getcallerpc(&b);
                runtime·racereadrangepc(b.array, b.len, pc, runtime·slicebytetostring);
        }
        s = gostringsize(b.len);
        runtime·memmove(s.str, b.array, s.len);
}

func slicebytetostringtmp(b Slice) (s String) {
        void *pc;

        if(raceenabled) {
                pc = runtime·getcallerpc(&b);
                runtime·racereadrangepc(b.array, b.len, pc, runtime·slicebytetostringtmp);
        }

        // Return a "string" referring to the actual []byte bytes.
        // This is only for use by internal compiler optimizations
        // that know that the string form will be discarded before
        // the calling goroutine could possibly modify the original
        // slice or synchronize with another goroutine.
        // Today, the only such case is a m[string(k)] lookup where
        // m is a string-keyed map and k is a []byte.
        s.str = b.array;
        s.len = b.len;
}

やっぱりコピーされるが,コンパイラの最適化でなんかやりたい雰囲気は見られた.
いまのところ,map のキーでキャストしているようなのにしかこの最適化は適用されないっぽい.

154
115
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
154
115