猿问

编译与手写程序集的性能差异

我一直在尝试在 Go 中使用汇编语言,并编写了一个汉明权函数作为练习。


我已经在这个 SO 答案上建立了一个本地 Go 版本,程序集版本基于来自 AMD (page 180) 的这个文档。在对这两个函数进行基准测试后,我发现原生 Go 版本比汇编版本快 1.5 到 2 倍,尽管手写汇编版本几乎与go tool 6g -S popcount.go.


输出 go test -bench=.


PASS

BenchmarkPopCount       100000000               19.4 ns/op 

BenchmarkPopCount_g     200000000                8.97 ns/op

ok      popcount   4.777s

popcount.go


package popcount


func popCount(i uint32) uint32 // Defined in popcount_amd64.s


func popCount_g(i uint32) uint32 {

    i = i - ((i >> 1) & 0x55555555)

    i = (i & 0x33333333) + ((i >> 2) & 0x33333333)

    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24

}

popcount_test.go


package popcount


import "testing"


func TestPopcount(t *testing.T) {

    for i := uint32(0); i < uint32(100); i++ {

        if popCount(i) != popCount_g(i) {

            t.Fatalf("failed on input = %v", i)

        }

    }

}


func BenchmarkPopCount(b *testing.B) {

    for i := 0; i < b.N; i++ {

        popCount(uint32(i))

    }

}


func BenchmarkPopCount_g(b *testing.B) {

    for i := 0; i < b.N; i++ {

        popCount_g(uint32(i))

    }

}

popcount_amd64.s


// func popCount(i uint32) uint32

TEXT ·popCount(SB),$0

    MOVL i+0(FP), BP        // i

    MOVL BP, BX             // i

    SHRL $1, BX             // i >> 1

    ANDL $0x055555555, BX   // (i >> 1) & 0x55555555

    SUBL BX, BP             // w = i - ((i >> 1) & 0x55555555)

    MOVL BP, AX             // w

    SHRL $2, BP             // w >> 2

    ANDL $0x033333333, AX   // w & 0x33333333

    ANDL $0x033333333, BP   // (w >> 2) & 0x33333333

输出 go tool 6g -S popcount.go


我从这里知道这些FUNCDATA行包含垃圾收集器的信息,但除此之外我没有看到任何明显的差异。


什么可能导致这两个功能之间的速度差异如此大?


慕无忌1623718
浏览 180回答 1
1回答
随时随地看视频慕课网APP

相关分类

Go
我要回答