2017-10-17 47 views
2

交換機的性能考慮這一基準,我們比較映射訪問VS交換機地圖VS中去

var code = []int32{0, 10, 100, 100, 0, 10, 0, 10, 100, 14, 1000, 100, 1000, 0, 0, 10, 100, 1000, 10, 0, 1000, 12} 
var mapCode = map[int32]int32{ 
    0: 1, 
    10: 2, 
    100: 3, 
    1000: 4, 
} 

func BenchmarkMap(b *testing.B) { 
    success := int32(0) 
    fail := int32(0) 
    for n := 0; n < b.N; n++ { 
     // for each value in code array, do a specific action 
     for _, v := range code { 
      c, ok := mapCode[v] 
      if !ok { 
       fail++ 
      } else { 
       success += c 
      } 
     } 
    } 
} 

func BenchmarkSwitch(b *testing.B) { 
    success := int32(0) 
    fail := int32(0) 
    for n := 0; n < b.N; n++ { 
     // for each value in code array, do a specific action 
     for _, v := range code { 
      switch v { 
      case 0: 
       success++ 
      case 10: 
       success += 2 
      case 100: 
       success += 3 
      case 1000: 
       success += 4 
      default: 
       fail++ 
      } 
     } 
    } 
} 

下面是結果:

BenchmarkMap-2   5000000   277 ns/op   0 B/op   0 allocs/op 
BenchmarkSwitch-2  30000000   48.2 ns/op   0 B/op   0 allocs/op 

因此,使用地圖似乎比開關方式慢。

目前,我想要使用類似於BenchmarkMap(),其中地圖訪問 是瓶頸的碼,以優化的功能,但作爲圖是動態生成的我不能使用開關當程序開始時(即,它 可能會根據輸入參數而改變)

有沒有辦法像動態生成的地圖那樣獲得與switch x {}類似的性能?

回答

6

與地圖不匹配,因爲indexing地圖在運行時進行評估,從地圖獲取元素涉及的操作多於單個(切片)索引。某些switch es(有case分支具有常量表達式)即使在編譯時也可以被優化。

但是地圖並不是唯一的「動態」結構。另一個,有slices。切片可以被索引,就像地圖一樣。

是的,切片是基礎陣列的連續區段的描述符。這意味着如果您有像1000這樣的索引,則該切片至少需要1000+1 = 1001個元素。

所以,如果你願意犧牲性能而一些內存,並使用切片的不是地圖,你甚至可以讓您的解決方案要比使用switch聲明的一個速度快:

var sliceCode = []int32{ 
    0: 1, 
    10: 2, 
    100: 3, 
    1000: 4, 
} 

func BenchmarkSlice(b *testing.B) { 
    success := int32(0) 
    fail := int32(0) 
    for n := 0; n < b.N; n++ { 
     // for each value in code array, do a specific action 
     for _, v := range code { 
      c := sliceCode[v] 
      if c == 0 { 
       fail++ 
      } else { 
       success += c 
      } 
     } 
    } 
} 

而基準測試結果:

BenchmarkMap-4   10000000    148 ns/op 
BenchmarkSlice-4  100000000    17.6 ns/op 
BenchmarkSwitch-4  50000000    31.0 ns/op 

在本具體例切片溶液通過被兩倍FA優於switch溶液ST!

注:

我上面提到的,如果你有一個像1000索引,你至少需要1001元素。這是部分事實。例如,如果你有像990..1000這樣的索引,你可以有一個簡單的索引轉換邏輯,如index - 990,然後只有11個元素的切片就足夠了。

另請注意,在使用逗號 - 成語表編制地圖時,我們能夠判斷元素是否在地圖中。用切片我們沒有這個選項。所以我們必須從元素類型的有效集合中指定一個值,並將其用作「缺失」信號。在上面的例子中,0對我們來說是完美的,因爲它沒有被使用(默認情況下,沒有明確列出的所有元素都被設置爲0)。如果在你的例子中可以使用所有有效的int32值,另一個選擇是使用包裝或指針類型作爲可能具有nil值的片段的元素類型,表示索引的元素缺失。

+0

我還提到'sort.Search',它是一個使用二分搜索算法在已排序的片段中進行搜索的通用工具。 – kostix

+0

@kostix是的,但在排序的片上使用二進制搜索很可能會比映射查找更差。 – icza

+0

剛剛在我的功能中實現它,並獲得了不錯的30%加速,非常感謝! – felix

1

有沒有辦法像動態生成的map一樣獲得與switch {{}類似的性能?

不,我很抱歉。

+0

@felix,...但您可以查看[代碼生成](https://blog.golang.org/generate)以自動生成大量「switch」分支 - 給定某些數據。 – kostix