KDOC 637: メモリ安全の境界検査のオーバーヘッドを見る
この文書のステータス
- 作成
- 2026-09-27 貴島
- レビュー
- 2026-09-27 貴島
概要
trace: unroll TraceIDFromHex/SpanIDFromHex decoders to eliminate bounds checks · Issue #8736 · open-telemetry/opentelemetry-goで、インデックスアクセスのオーバーヘッドの存在と、コンパイラでの確認方法について説明していた。
実行して確認した。
set -e
d=$(mktemp -d)
cat > "$d/go.mod" <<'EOF'
module bcedemo
go 1.26.0
EOF
cat > "$d/bce.go" <<'EOF'
package bcedemo
// BCE 効かない: i+1,i+2,i+3 を範囲内と証明できず検査が残る
func sumLoop(a []byte) byte {
if len(a) != 8 {
return 0
}
var s byte
for i := 0; i < len(a); i += 4 {
s += a[i] + a[i+1] + a[i+2] + a[i+3]
}
return s
}
// BCE 効く: len==8 ガード後、定数 index だと全検査が消える
func sumUnrolled(a []byte) byte {
if len(a) != 8 {
return 0
}
return a[0] + a[1] + a[2] + a[3] + a[4] + a[5] + a[6] + a[7]
}
EOF
cat > "$d/bce_test.go" <<'EOF'
package bcedemo
import "testing"
var input = make([]byte, 8)
var sink byte
func BenchmarkSumLoop(b *testing.B) { for i := 0; i < b.N; i++ { sink = sumLoop(input) } }
func BenchmarkSumUnrolled(b *testing.B) { for i := 0; i < b.N; i++ { sink = sumUnrolled(input) } }
EOF
cd "$d"
echo "== (1) 境界検査 =="
go build -gcflags='-d=ssa/check_bce/debug=1' . 2>&1 | grep bce.go
echo "== (2) 速度差 =="
go test -run='^$' -bench='^BenchmarkSum' -benchmem -count=4 . 2>&1 | grep -E 'Benchmark|ok'
cd / && rm -rf "$d"
== (1) 境界検査 ==
./bce.go:10:9: Found IsInBounds
./bce.go:10:16: Found IsInBounds
./bce.go:10:25: Found IsInBounds
./bce.go:10:34: Found IsInBounds
== (2) 速度差 ==
BenchmarkSumLoop-32 669234343 1.781 ns/op 0 B/op 0 allocs/op
BenchmarkSumLoop-32 657591210 1.782 ns/op 0 B/op 0 allocs/op
BenchmarkSumLoop-32 663465331 1.794 ns/op 0 B/op 0 allocs/op
BenchmarkSumLoop-32 649490074 1.771 ns/op 0 B/op 0 allocs/op
BenchmarkSumUnrolled-32 1000000000 0.8872 ns/op 0 B/op 0 allocs/op
BenchmarkSumUnrolled-32 1000000000 0.8967 ns/op 0 B/op 0 allocs/op
BenchmarkSumUnrolled-32 1000000000 0.8790 ns/op 0 B/op 0 allocs/op
BenchmarkSumUnrolled-32 1000000000 0.8782 ns/op 0 B/op 0 allocs/op
ok bcedemo 9.372s
変数インデックス部分にのみ IsInBounds が発生し、定数インデックスには発生してないことがわかる。ビルド時に挿入されて、実行時に検証される分のオーバーヘッドがある。定数インデックス時には挿入されずにオーバーヘッドがない。
Go本体に、挿入されないケースの確認テストがある。
https://github.com/golang/go/blob/8fc7e76411ed6db7a351db1e4be762cdc5350de5/test/checkbce.go#L9-L23
// Test that the compiler does bounds check elimination as expected.
// This avoids accidental regressions.
package main
import "encoding/binary"
func f0(a []int) {
a[0] = 1 // ERROR "Found IsInBounds$"
a[0] = 1
a[6] = 1 // ERROR "Found IsInBounds$"
a[6] = 1
a[5] = 1
a[5] = 1
}