gpt4 book ai didi

戈朗 : index efficiency of array

转载 作者:IT王子 更新时间:2023-10-29 01:06:33 24 4
gpt4 key购买 nike

这是一个简单的程序。测试环境:debian 8,go 1.4.2

联合.go:

package main

import "fmt"

type A struct {
t int32
u int64
}

func test() (total int64) {
a := [...]A{{1, 100}, {2, 3}}

for i := 0; i < 5000000000; i++ {
p := &a[i%2]
total += p.u
}
return
}
func main() {
total := test()
fmt.Println(total)
}

联合.c:

#include <stdio.h>

struct A {
int t;
long u;
};

long test()
{
struct A a[2];
a[0].t = 1;
a[0].u = 100;
a[1].t = 2;
a[1].u = 3;

long total = 0;
long i;
for (i = 0; i < 5000000000; i++) {
struct A* p = &a[i % 2];
total += p->u;
}
return total;
}
int main()
{
long total = test();
printf("%ld\n", total);
}

结果比较:

Go:

257500000000

real 0m9.167s
user 0m9.196s
sys 0m0.012s

C:

257500000000

real 0m3.585s
user 0m3.560s
sys 0m0.008s

go好像编译了很多奇怪的汇编代码(可以用objdump -D查看)

例如,为什么 movabs $0x12a05f200,%rbp 出现两次?

  400c60:       31 c0                   xor    %eax,%eax
400c62: 48 bd 00 f2 05 2a 01 movabs $0x12a05f200,%rbp
400c69: 00 00 00
400c6c: 48 39 e8 cmp %rbp,%rax
400c6f: 7d 46 jge 400cb7 <main.test+0xb7>
400c71: 48 89 c1 mov %rax,%rcx
400c74: 48 c1 f9 3f sar $0x3f,%rcx
400c78: 48 89 c3 mov %rax,%rbx
400c7b: 48 29 cb sub %rcx,%rbx
400c7e: 48 83 e3 01 and $0x1,%rbx
400c82: 48 01 cb add %rcx,%rbx
400c85: 48 8d 2c 24 lea (%rsp),%rbp
400c89: 48 83 fb 02 cmp $0x2,%rbx
400c8d: 73 2d jae 400cbc <main.test+0xbc>
400c8f: 48 6b db 10 imul $0x10,%rbx,%rbx
400c93: 48 01 dd add %rbx,%rbp
400c96: 48 8b 5d 08 mov 0x8(%rbp),%rbx
400c9a: 48 01 f3 add %rsi,%rbx
400c9d: 48 89 de mov %rbx,%rsi
400ca0: 48 89 5c 24 28 mov %rbx,0x28(%rsp)
400ca5: 48 ff c0 inc %rax
400ca8: 48 bd 00 f2 05 2a 01 movabs $0x12a05f200,%rbp
400caf: 00 00 00
400cb2: 48 39 e8 cmp %rbp,%rax
400cb5: 7c ba jl 400c71 <main.test+0x71>
400cb7: 48 83 c4 20 add $0x20,%rsp
400cbb: c3 retq
400cbc: e8 6f e0 00 00 callq 40ed30 <runtime.panicindex>
400cc1: 0f 0b ud2
...

而 C 程序集更干净:

0000000000400570 <test>:
400570: 48 c7 44 24 e0 64 00 movq $0x64,-0x20(%rsp)
400577: 00 00
400579: 48 c7 44 24 f0 03 00 movq $0x3,-0x10(%rsp)
400580: 00 00
400582: b9 64 00 00 00 mov $0x64,%ecx
400587: 31 d2 xor %edx,%edx
400589: 31 c0 xor %eax,%eax
40058b: 48 be 00 f2 05 2a 01 movabs $0x12a05f200,%rsi
400592: 00 00 00
400595: eb 18 jmp 4005af <test+0x3f>
400597: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)
40059e: 00 00
4005a0: 48 89 d1 mov %rdx,%rcx
4005a3: 83 e1 01 and $0x1,%ecx
4005a6: 48 c1 e1 04 shl $0x4,%rcx
4005aa: 48 8b 4c 0c e0 mov -0x20(%rsp,%rcx,1),%rcx
4005af: 48 83 c2 01 add $0x1,%rdx
4005b3: 48 01 c8 add %rcx,%rax
4005b6: 48 39 f2 cmp %rsi,%rdx
4005b9: 75 e5 jne 4005a0 <test+0x30>
4005bb: f3 c3 repz retq
4005bd: 0f 1f 00 nopl (%rax)

谁能解释一下?谢谢!

最佳答案

主要区别在于数组边界检查。在 Go 程序的反汇编转储中,有:

400c89:       48 83 fb 02             cmp    $0x2,%rbx
400c8d: 73 2d jae 400cbc <main.test+0xbc>
...
400cbc: e8 6f e0 00 00 callq 40ed30 <runtime.panicindex>
400cc1: 0f 0b ud2

因此,如果 %rbx 大于或等于 2,那么它会跳转到对 runtime.panicindex 的调用。鉴于您正在使用大小为 2 的数组,这显然是边界检查。您可以提出这样的论点,即编译器应该足够聪明,可以在这种可以静态确定索引范围的特定情况下跳过边界检查,但它似乎还不够聪明,无法这样做。

虽然您看到此微基准测试有明显的性能差异,但可能值得考虑这是否真的代表了您的实际代码。如果您在循环中做其他事情,差异可能不太明显。

虽然边界检查确实有成本,但在许多情况下,它比程序继续未定义行为的替代方案要好。

关于戈朗 : index efficiency of array,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31555672/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com