Go の標準パッケージのコードには稀に意図的にそうなっているのか分からない、速度に寄与するのかどうか確かめたくなる物が入っている事があります。
先日も見つけました。まだマージされてないですが、os.Mkdir
に NUL という文字列を渡した時にファーストパスでエラーを返す変更です。
186139: os: return an error when the argument of Mkdir on Windows is os.DevNull
https://go-review.googlesource.com/c/go/+/186139/5/src/os/file.go#563
ここで出てくる以下のコード。
func isDevNull(name string) bool
if len(name) != 3 {
return false
}
if name[0]|0x20 != 'n' {
return false
}
if name[1]|0x20 != 'u' {
return false
}
if name[2]|0x20 != 'l' {
return false
}
return true
}
3文字の NUL を大文字小文字無視で比較しています。ビットマスクで大文字小文字を同一視しつつ、1つでも条件にマッチしない物があれば即 false を返すという古き良きC言語的なハックが使われています。
さて、このコードは本当に速度に寄与するのでしょうか?
package lowercase
import (
"strings"
"testing"
)
func isDevNull1(name string) bool {
if len(name) != 3 {
return false
}
if name[0]|0x20 != 'n' {
return false
}
if name[1]|0x20 != 'u' {
return false
}
if name[2]|0x20 != 'l' {
return false
}
return true
}
func isDevNull2(name string) bool {
if len(name) != 3 {
return false
}
if name[0] != 'n' && name[0] != 'N' {
return false
}
if name[1] != 'u' && name[1] != 'U' {
return false
}
if name[2] != 'l' && name[2] != 'L' {
return false
}
return true
}
func isDevNull3(name string) bool {
return strings.ToLower(name) == "nul"
}
var tests = []struct {
in string
result bool
}{
{"nul", true},
{"Nul", true},
{"nui", false},
{"lun", false},
{"nulllllllllllllll", false},
{"nuuuuuuuul", false},
{strings.Repeat("N", 3000), false},
}
func test(b *testing.B, f func(string) bool) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
for _, test := range tests {
if got := f(test.in); got != test.result {
b.Fatalf("want %v but got %v for %v", test.result, got, test.in)
}
}
}
}
func BenchmarkS1(b *testing.B) {
test(b, isDevNull1)
}
func BenchmarkS2(b *testing.B) {
test(b, isDevNull2)
}
func BenchmarkS3(b *testing.B) {
test(b, isDevNull3)
}
isDevNull1 が今回のコード、isDevNull2 が改良前のコード、isDevNull3 が入力文字を予め小文字に変換し比較するコードです。ベンチマークの実行結果は以下の通り。
goos: windows
goarch: amd64
pkg: github.com/mattn/go-sandbox/b3
BenchmarkS1-4 47997120 25.5 ns/op
BenchmarkS2-4 41377027 28.5 ns/op
BenchmarkS3-4 136354 8786 ns/op
PASS
ok github.com/mattn/go-sandbox/b3 3.840s
Windows 64bit Core i7 16GB の結果です。今回改良されるコードが微妙ながら速度に寄与している事が分かりました。逆に言えばこの程度しか寄与していないので、可読性を優先する様なコードであれば isDevNull2 で充分かなとも思います。