1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

dtruss(dtrace)を使って、みんなのGo言語に載っているコードを試してみる

Last updated at Posted at 2018-09-27

現在大きめの会社でSREチームとして働いています。
範囲はモバイルアプリから、インフラまで色々やります。

今回はSRE関連でパフォーマンスチューニングのテスト自動化ツールを作っているときに、ファイルにjsonを書き出す必要があったため、そこでのパフォーマンスについて、「みんなのGo言語」という本の3.3章で触れていたので、それをdtrussで試してみました。

環境

Mac 10.12 Sierra
※dtrussはバージョンにもよりますがデフォルトで入っています

code

https://github.com/revenue-hack/go-sample
「みんなのGo言語」(以下みんGo)の3.3の効率的なI/O処理という部分の話

説明

今回はGoでバッファリングすることでwriteのシステムコールが減らせたことを見るので、dtrussの詳しい説明は省きます。とりあえずコマンドのシステムコールをtraceするものと思っておけば大丈夫です。

まずGoでは自動的にバッファリングは行われません。
LL系だと場合によって自動でバッファリングが行われるのですが、Goはそのような仕組みはないので意図的にバッファリングをする必要があります。
早速みんGoで書かれているコードをそのまま使ってdtraceでシステムコールを確認していきます。

バッファリングしない場合

func noBuffer() {
	// wirteを100回call
	for i := 0; i < 100; i++ {
		fmt.Fprintln(os.Stdout, strings.Repeat("x", 100))
	}
}

ただ「xxxx......」を表示しているだけです。
実際にこのコードを実行した際のtraceを見てみます。

 go build buffer_bench.go
 sudo dtruss -c ./buffer_bench

結果

CALL                                        COUNT
bsdthread_register                              1
close                                           1
exit                                            1
open                                            1
read                                            1
stat64                                          2
sysctl                                          3
bsdthread_create                                4
mmap                                            9
sigaltstack                                    10
__pthread_sigmask                              14
sigaction                                      50
select                                         58
write                                         100
write                                         100

システムコールのwriteが100回行われたことを意味します。

バッファリングした場合

func buffer() {
	// bufferによってwriteのcallを少なくできる
	buf := bufio.NewWriter(os.Stdout)
	for i := 0; i < 100; i ++ {
		fmt.Fprintln(buf, strings.Repeat("x", 100))
	}
	buf.Flush()
}

続いてみんごGoに書かれている通り、バッファリングした場合は上記のようなコードになります。
NewWriterにはio.Readerを引数として渡すので、ここにファイルポインタなり、os.Stdoutなりを渡すようにします。
また最後にbuf.Flush()するのはbufferの残を全て吐き出すためです。
では以下がdtraceの結果です。

CALL                                        COUNT
bsdthread_register                              1
close                                           1
exit                                            1
open                                            1
read                                            1
stat64                                          2
sysctl                                          3
write                                           3
bsdthread_create                                4
mmap                                            8
sigaltstack                                    10
__pthread_sigmask                              14
select                                         32
sigaction                                      50
write                                           3

するとwriteは3回になっています。
挙動としてはbufferを使ってない場合と同じなのですが、バッファリングしたことで、バッファが溜まったときにwriteのシステムコールがcallされるので、writeのコールは格段と減り、パフォーマンスの向上に繋がります。

以上となります。
バッファリングはパフォーマンスでは欠かせないので、気をつけていきましょう。
またこちら、参考になればと思います。
https://github.com/revenue-hack/go-sample

参考

1
2
0

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
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?