golang メモ
golang についてのメモ。適宜追加。
目次
組み込み関数
ドキュメント
下記にまとまっている。
builtin - The Go Programming Language
string
アクセス方法ごとの返り値
...
で展開した際はbyteが返るので、下記のようにしてbyte sliceにappendできる。
a := []byte{} b := "hoge" a = append(a, b...)
stringは配列のようにアクセスするとbyteが返る。またstringのサイズをlenで求めた場合、byte数が返る。
一方で、for rangeで回すとruneが返る。
よって、下記のようにループの回し方でそれぞれ異なる値が返る。
package main import "fmt" func main() { test := "hoge" for i := 0; i < len(test); i++ { fmt.Printf("type=%T val=%x\n", test[i], test[i]) } for _, v := range test { fmt.Printf("type=%T val=%c\n", v, v) } }
type=uint8 val=68 type=uint8 val=6f type=uint8 val=67 type=uint8 val=65 type=int32 val=h type=int32 val=o type=int32 val=g type=int32 val=e
slice
lengthとcapacity
sliceはlengthとcapacity を持つ。それぞれlen()
および cap()
というビルトイン関数で取得できる。いずれも戻り値はint型。
makeする際にcapcityを省略すると、capacityの値はlengthと等しくなる。
a := make([]int, 8) fmt.Printf("len=%d cap=%d\n", len(a), cap(a)) // len=8 cap=8
sliceは参照型
参照型なので、代入してから変更すると、元のsliceも更新される。
func printSlicePointer() { a := []byte{0xaa} b := a fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", a, b, a, b) b[0] = 0x12 fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", a, b, a, b) }
これの結果は下記。アドレスならびにbへの修正がaに対しても行われたかのように見える。
a=0xc00001a110 b=0xc00001a110 a=aa b=aa a=0xc00001a110 b=0xc00001a110 a=12 b=12
これを避けるにはcopyを使用して、同一内容のsliceを別途作成する。
sliceのcopy
// copy元のslice が var src []byteとする dst := make([]byte, len(src) ) copy(dst, src)
dstに十分なlengthがない場合、copyでpanicする。
sliceの一部を参照する
dst := src[start:end]
これによって、src[start] から src[end-1]の要素を参照する slice dst が生成される。len(dst) は end - startとなる。
参照なので、dstを修正した場合は、src側も修正される。
startとendは省略も可能。startを省略した場合は先頭から、endを省略した場合は最後尾まで、という意味になる。
array
宣言
a := [...]int{0, 1, 2, 3}
arrayを参照するsliceの生成
slice := array[:]
下記のようなコードを実行すると、sliceとarrayで同じアドレスを指しており、sliceはarrayの参照であることがわかる。
a := [4]int{0, 1, 2, 3} b := a[:] fmt.Printf("a=%p b=%p\n a=%x\n b=%x\n", &a, b, a, b)
この結果は下記。
a=0xc000014460 b=0xc000014460 a=[0 1 2 3] b=[0 1 2 3]
interface
cast
vInt, ok := i.(int)
switch
switch v := i.(type) { case int: fmt.Printf("int\n") case string: fmt.Printf("string\n") default: fmt.Printf("unknown\n") }
構造体のTag
encoding/json でよく見る。`key:"val"`
type sample struct { i int `json:"int"` f float32 `sample:"float"` }
バッククォート内ではスペースをつけて複数のタグを付与することもできる。
type sample struct { i int `json:"int" sample:"aaa"`
The Go Programming Language Specification - The Go Programming Language
タグの取得
reflectパッケージを使用する。reflect.StructFieldを取得し、そのメンバのTagを参照する。
- TypeOfでTypeを取得
- Kindで構造体か判別し、NumFieldで構造体個数を確認する。
- Fieldで構造体メンバを取得し、TagメンバのStructTagに対して、Get/Lookupする。
func readStructTag() { type S struct { V int `sample:"V"` W string `sample:"W"` I uint B float32 `sample:""` } s := S{} t := reflect.TypeOf(s) for i := 0; i < t.NumField(); i++ { f := t.Field(i) v, ok := f.Tag.Lookup("sample") if ok { fmt.Printf("%d:value=%s\n", i, v) } } }
0:value=V 1:value=W 3:value=
reflect - The Go Programming Language
cgo
公式Wiki
コメント
コンパイラディレクティブ
compile - The Go Programming Language
ビルド
https://golang.org/cmd/go/#hdr-Build_constraints
テスト
$ go test
サブディレクトリ以下もテストをする場合は、./...
を付与する。
$ go test ./...
コメントによる標準出力のテスト
// Output:
というコメントを付与し、改行後想定される出力をコメントで記述する。
//
の後にはスペースを挟む。
// Output: // 0x0f
出力内容が順不同の場合は、// Unordered output:
とする。
testing - The Go Programming Language
ベンチマーク
BenchmarkXxx(*testing.B) という関数を定義する。 Xxxとあるように、大文字である必要がある。
testing - The Go Programming Language
go test
にオプションを付与することでベンチマークが測定される。
go - The Go Programming Language
メモリ使用量やアロケート回数を調べたい場合は下記。
go test -bench . --benchmem
プロファイルを取得する
[Go] pprofでのプロファイル(計測)のやり方を改めて整理した - Qiita
go test
時に下記オプションを付与することで、go tool pprof
用のファイルを生成できる。
go test -cpuprofile cpu.prof -memprofile mem.prof -bench .
ビルド
Cから使える共有ライブラリをビルドする
-buildmode=c-shared
を付与する。
go - The Go Programming Language
最適化状況を確認する
-gcflags "-m"
をつける。
Goのコンパイル時の最適化結果を確認する(インライン化の条件についても記載) - Qiita
最適化されるための条件。
CompilerOptimizations · golang/go Wiki · GitHub
コンパイルオプション一覧
compile - The Go Programming Language
golang 自体のビルド
リポジトリをチェックアウトして、バージョンbranchにスイッチ。
$ git clone https://go.googlesource.com/go goroot $ cd goroot $ git checkout go1.XX.X $ cd src $ ./all.bash
参考資料
- 最適化/高速化手法が複数公開されている。