Bounds Check Eliminationについて調べてみた / 1218-lt

Bf32ae94d9e03c03c963f364f2b47698?s=47 kaznishi
December 18, 2018

Bounds Check Eliminationについて調べてみた / 1218-lt

Bf32ae94d9e03c03c963f364f2b47698?s=128

kaznishi

December 18, 2018
Tweet

Transcript

  1. Bounds Check Eliminationに ついて調べてみた 2018-12-18 golang.tokyo #20 LT by kaznishi

  2. 自己紹介 Twitter: @kaznishi1246 主な守備範囲: サーバーサイド,インフラ 主な使用言語: Go, PHP, Scala

  3. はじめに 配列やスライスに対して添字アクセス 通常は範囲チェックが必要 範囲チェック...Bounds Check

  4. はじめに しかし、コンパイラが安全だと判断する状況下で はチェックの省略が可能 範囲チェックの省略のことを Bounds Check Elimination(BCE)という Go 1.7+ ではSSAが導入されてBCEが動くようにな

    った
  5. func f1(nums []int) int { sum := nums[0] sum +=

    nums[1] sum += nums[2] sum += nums[3] sum += nums[4] return sum } func f2(nums []int) int { sum := nums[4] sum += nums[3] sum += nums[2] sum += nums[1] sum += nums[0] return sum } どちらもスライスの要素を足し合わせる処理
  6. build実行時に -gcflags="-d=ssa/check_bce/debug=1" というオプションを付けてみよう

  7. $ go build -gcflags="-d=ssa/check_bce/debug=1" main.go # command-line-arguments ./main.go:11:13: Found IsInBounds

    ./main.go:12:13: Found IsInBounds ./main.go:13:13: Found IsInBounds ./main.go:14:13: Found IsInBounds ./main.go:15:13: Found IsInBounds ./main.go:20:13: Found IsInBounds
  8. func f1(nums []int) int { sum := nums[0] # Found

    IsInBounds sum += nums[1] # Found IsInBounds sum += nums[2] # Found IsInBounds sum += nums[3] # Found IsInBounds sum += nums[4] # Found IsInBounds return sum } func f2(nums []int) int { sum := nums[4] # Found IsInBounds sum += nums[3] sum += nums[2] sum += nums[1] sum += nums[0] return sum }
  9. func f1(nums []int) int { sum := nums[0] # Found

    IsInBounds sum += nums[1] # Found IsInBounds sum += nums[2] # Found IsInBounds sum += nums[3] # Found IsInBounds sum += nums[4] # Found IsInBounds return sum } func f2(nums []int) int { sum := nums[4] # Found IsInBounds sum += nums[3] sum += nums[2] sum += nums[1] sum += nums[0] return sum } IsInBounds (IsSliceInBounds) ... Bounds Checkが必要
  10. func f2(nums []int) int { sum := nums[4] # 先に[4]にアクセスすることで

    sum += nums[3] # チェック不要 sum += nums[2] # チェック不要 sum += nums[1] # チェック不要 sum += nums[0] # チェック不要 return sum }
  11. ベンチ

  12. func BenchmarkF1(b *testing.B) { nums := []int{} for i :=

    0; i < 5; i++ { nums = append(nums, i) } for n := 0; n < b.N; n++ { _ = f1(nums) } } func BenchmarkF2(b *testing.B) { nums := []int{} for i := 0; i < 5; i++ { nums = append(nums, i) } for n := 0; n < b.N; n++ { _ = f2(nums) } }
  13. BenchmarkF1-4 2000000000 0.99 ns/op BenchmarkF2-4 2000000000 0.33 ns/op

  14. BCEが適用される他のケース

  15. if, lenで確認してから func f3(s []int, index int) { if index

    >= 0 && index < len(s) { _ = s[index] // Bounds Check Elimination!! _ = s[index:len(s)] // Bounds Check Elimination!! } } func f4(s []int) { if len(s) > 2 { _, _, _ = s[0], s[1], s[2]//Bounds Check Elimination!! } }
  16. forループ中でのアクセス func f5(s []int) { for i := range s

    { _ = s[i] // Bounds Check Elimination!! _ = s[i:len(s)] // Bounds Check Elimination!! _ = s[:i+1] // Bounds Check Elimination!! } } func f6(s []int) { for i := 0; i < len(s); i++ { _ = s[i] // Bounds Check Elimination!! _ = s[i:len(s)] // Bounds Check Elimination!! _ = s[:i+1] // Bounds Check Elimination!! } }
  17. ただし、別の配列やスライスへのアクセスはBCEは適 用されない func f7a(s []int) []int { var hoge int

    s2 := make([]int, len(s)) for i := range s { hoge = -s[i] s2[i] = hoge // Bounds Checkが行われる } return s2 } make([]int, len(s)) なので人の頭で考えれば安全なの は分かるが…
  18. ちょっとしたテクニック func f7b(s []int) []int { var hoge int s2

    := make([]int, len(s)) s2 = s2[:len(s)] // これがキモ for i := range s { hoge = -s[i] s2[i] = hoge // Bounds Check Elimination!! } return s2 }
  19. ただしベンチの改善はなかった… func BenchmarkF7A(b *testing.B) { nums := []int{} for i

    := 0; i < 50000; i++ { nums = append(nums, i) } for n := 0; n < b.N; n++ { _ = f7a(nums) } } func BenchmarkF7B(b *testing.B) { nums := []int{} for i := 0; i < 50000; i++ { nums = append(nums, i) } for n := 0; n < b.N; n++ { _ = f7b(nums) } }
  20. ただしベンチの改善はなかった… BenchmarkF7A-4 20000 79592 ns/op BenchmarkF7B-4 20000 85497 ns/op

  21. BCEを使って性能改善した例 https://github.com/disintegration/imaging/pull/81 Great Work!!

  22. 所感 for,if,lenで配列やスライスの安全なコードを書くこ とでBCE適用 特にBCEを意識しなくても知らぬ間に適用され ている可能性が大きい 意図的にBCEを適用させるテクニックはベンチを 取ったうえで使いましょう チューニングの手段のひとつとして頭に置いてお くのはよいと思う

  23. 参考記事 Bounds Check Elimination https://go101.org/article/bounds-check- elimination.html

  24. おわり