こんにちは。KOUKIです。
Web系企業で、Go言語を使ったAPIの開発業務を行なっています。そして、Go言語について得られた知見を記事にしています。
今日は、ポインタを使った速度改善についてお話しします。
<目次>
ポインタで速度改善
Go言語はポインタを扱うことができます。ポインタは値のメモリアドレスを指します。
このポインタ、実はGo言語の処理速度に大きく関係するものなのです。
「ポインタを使うとメモリの消費を抑えられる」という話はよく聞きますが、恥ずかしながら、速度改善に繋がることは知りませんでした。
というか、今まで具体的なサンプルをみたことがなかったので、意識すらしていませんでした。
ポインタを使わないコード
ポインタを使わなかった時の速度を簡単に計測できるコードを用意しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package main import "fmt" type huge struct { haves [10000000]string } func (h huge) addr() { fmt.Printf("%p\n", &h) } func main() { var h huge for i := 0; i < 10; i++ { h.addr() } } |
上記のコードでは、固定長「10000000」の配列を持ったhuge構造体を用意しています。
そして、addr()メソッドにて、huge構造体のアドレスをコンソール上に表示します。
main関数内では、このaddr()メソッドを10回呼び出しています。
terminal上で、以下のコマンドを実行してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ time go run main.go 0xc009a00000 0xc013300000 0xc01cb98000 0xc009a00000 0xc013300000 0xc009a00000 0xc01cb98000 0xc009a00000 0xc013300000 0xc009a00000 real 0m2.270s user 0m1.982s sys 0m0.663s |
処理が完了するまで、2.270sかかりました。
ポインタを使ったコード
それでは、先ほどのaddr()メソッドをポインタを使った形式に書き換えてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
package main import "fmt" type huge struct { haves [10000000]string } // ポインタ(*)を使った形式に書き換え func (h *huge) addr() { fmt.Printf("%p\n", &h) } func main() { var h huge for i := 0; i < 10; i++ { h.addr() } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$ time go run main.go 0xc000096000 0xc000096010 0xc000096018 0xc000096020 0xc000096028 0xc000096030 0xc000096038 0xc000096040 0xc000096048 0xc000096050 real 0m0.347s user 0m0.267s sys 0m0.144s |
今度は、処理が完了するまでに0.347sしかかかりませんでした。
速度が桁違いにアップしました!
考察
addr()メソッドの「(h *huge) 」の部分をレシーバーと呼びますが、ポインタを使っていない場合、このレシーバーは、hのコピーを受け取ります。
hとはすなわち「haves [10000000]string」であり、かなり大容量のデータをループが回るたびに呼び出しています。
しかし、ポインタを使った場合、アドレス情報を示す容量の少ないポインタデータを渡すだけなので、速度が跳ね上がったわけです。※ポインタはメモリのアドレスを示す
おわりに
Go言語はものすごく早いシステムを構築するのに向いている言語です。
そのため、私も日々、速度改善を意識したコードを書く必要に迫られています。
これからもこういうナレッジを共有したいと思います。
それでは、また!
最近のコメント