Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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 } どちらもスライスの要素を足し合わせる処理

Slide 6

Slide 6 text

build実行時に -gcflags="-d=ssa/check_bce/debug=1" というオプションを付けてみよう

Slide 7

Slide 7 text

$ 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

Slide 8

Slide 8 text

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 }

Slide 9

Slide 9 text

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が必要

Slide 10

Slide 10 text

func f2(nums []int) int { sum := nums[4] # 先に[4]にアクセスすることで sum += nums[3] # チェック不要 sum += nums[2] # チェック不要 sum += nums[1] # チェック不要 sum += nums[0] # チェック不要 return sum }

Slide 11

Slide 11 text

ベンチ

Slide 12

Slide 12 text

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) } }

Slide 13

Slide 13 text

BenchmarkF1-4 2000000000 0.99 ns/op BenchmarkF2-4 2000000000 0.33 ns/op

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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!! } }

Slide 16

Slide 16 text

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!! } }

Slide 17

Slide 17 text

ただし、別の配列やスライスへのアクセスは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)) なので人の頭で考えれば安全なの は分かるが…

Slide 18

Slide 18 text

ちょっとしたテクニック 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 }

Slide 19

Slide 19 text

ただしベンチの改善はなかった… 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) } }

Slide 20

Slide 20 text

ただしベンチの改善はなかった… BenchmarkF7A-4 20000 79592 ns/op BenchmarkF7B-4 20000 85497 ns/op

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

おわり