gpt4 book ai didi

c - 基准 C 结构比较 : XOR vs ==

转载 作者:行者123 更新时间:2023-12-04 08:22:37 26 4
gpt4 key购买 nike

假设我们在 C 中有一个简单的结构,它有 4 个字段:

typedef struct {
int a;
int b;
int c;
int d;
} value_st;
让我们来看看这两个简短版本的 C 结构相等检查。
第一个是直接的,并执行以下操作:
int compare1(const value_st *x1, const value_st *x2) {
return ( (x1->a == x2->a) && (x1->b == x2->b) &&
(x1->c == x2->c) && (x1->d == x2->d) );
}
第二个使用 异或 :
int compare2(const value_st *x1, const value_st *x2) {
return ( (x1->a ^ x2->a) | (x1->b ^ x2->b) |
(x1->c ^ x2->c) | (x1->d ^ x2->d);
}
第一个版本会返回 非零 如果两个结构相等。
第二个版本会返回 如果两个结构相等。
编译器输出
使用 GCC -O2 编译并检查程序集看起来像我们期望的那样。
第一个版本是4条CMP指令和JMPS:
xor    %eax,%eax
mov (%rsi),%edx
cmp %edx,(%rdi)
je 0x9c0 <compare1+16>
repz retq
nopw 0x0(%rax,%rax,1)
mov 0x4(%rsi),%ecx
cmp %ecx,0x4(%rdi)
jne 0x9b8 <compare1+8>
mov 0x8(%rsi),%ecx
cmp %ecx,0x8(%rdi)
jne 0x9b8 <compare1+8>
mov 0xc(%rsi),%eax
cmp %eax,0xc(%rdi)
sete %al
movzbl %al,%eax
retq
第二个版本看起来像这样:
mov    (%rdi),%eax
mov 0x4(%rdi),%edx
xor (%rsi),%eax
xor 0x4(%rsi),%edx
or %edx,%eax
mov 0x8(%rdi),%edx
xor 0x8(%rsi),%edx
or %edx,%eax
mov 0xc(%rdi),%edx
xor 0xc(%rsi),%edx
or %edx,%eax
retq
所以 第二个版本有:
  • 无分行
  • 少说明

  • 基准测试
    static uint64_t
    now_msec() {
    struct timespec spec;
    clock_gettime(CLOCK_MONOTONIC, &spec);
    return ((uint64_t)spec.tv_sec * 1000) + (spec.tv_nsec / 1000000);
    }

    void benchmark() {
    uint64_t start = now_msec();
    uint64_t sum = 0;

    for (uint64_t i = 0; i < 1e10; i++) {
    if (compare1(&x1, &x2)) {
    sum++;
    }
    }

    uint64_t delta_ms = now_msec() - start;

    // use sum and delta here
    }
    足够的迭代来过滤掉调用clock_gettime()所需的时间
    但这是我不明白的事情......
    当我进行基准测试时 等于 需要执行所有指令的结构体,
    第一个版本更快...
    time took for compare == is 3114 [ms]   [matches: 10000000000]
    time took for compare XOR is 3177 [ms] [matches: 10000000000]
    这怎么可能 ?
    即使有分支预测,XOR 也应该是超快速指令和
    不输给 CMP/JMP
    更新
    几个重要的注意事项:
  • 这个问题主要是给了解 结果。不要试图击败编译器或创建晦涩难懂的代码 - 编写干净的代码并让编译器优化总是更好
  • 我们假设结构体在 中缓存 , 否则 支配 因素显然是内存查找
  • 分支预测显然会起到一定的作用……但是它会比无分支代码更好吗(假设大部分时间我们都执行所有代码)?
  • memcmp将需要在结构中填充零,并且在大多数标准实现中也可能需要一个循环/if,因为它支持可变大小比较

  • 更新 2
  • 许多人表示,差异每次调用都很小...这是真的,但 一致 这意味着这种差异有利于许多连续运行中的第一个版本

  • 更新 3
    我已将我的测试代码复制到带有 Intel(R) Xeon(R) CPU E5-2667 v3 @ 3.20GHz 的实验室服务器上。
    XOR 版本在 GCC 8 的服务器上运行速度几乎快两倍。
    与 clang 和 GCC 8 一起尝试:
    对于海湾合作委员会 8:
    time took for compare == is 7432 [ms]    [matches: 3000000000]
    time took for compare XOR is 4214 [ms] [matches: 3000000000]
    对于铿锵:
    time took for compare == is 4265 [ms]    [matches: 3000000000]
    time took for compare XOR is 5508 [ms] [matches: 3000000000]
    所以这似乎非常依赖编译器和 CPU。

    最佳答案

    好吧,在第一种情况下,有 4 个 mov 和 4 个 cmp。在第二种情况下,有 4 个 mov、4 个异或和 4 个或。由于 jmp 未采取立即生效,因此第一个版本更快。 (cmp 和 xor 做的事情基本相同,应该在相同的时间内执行)
    这个故事的寓意是你永远不应该试图超越你的编译器,它真的知道更好(至少在 99.99% 的情况下)
    并且永远不要为了让它更快而模糊你的程序的意图,除非你有确凿的证据它是 (1) 需要和 (2) 有效的。

    关于c - 基准 C 结构比较 : XOR vs ==,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65424506/

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