2017-10-17 48 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