是三元函数还是if语句更快?

问题描述 投票:-1回答:1

所以我有两个选择,两个函数的类型都一样。

(Entry->d_type == DT_DIR ? rmdirr : remove)(CurrentEntryPath);

Or

if (Entry->d_type == DT_DIR) {
    rmdirr(CurrentEntryPath);
} else {
    remove(CurrentEntryPath);
}

我已经确认三元函数是百分之百安全的,因为两个函数都是兼容的指针类型。哪一个更快(即使可读性更低)?

c performance function if-statement conditional-operator
1个回答
5
投票

很难判断哪种方式实际上更有效率。if-else产生的指令较少,但有一个分支指令,如果不满足分支预测,需要进行流水线刷新。

#define SOMEVALUE 5


int __attribute__((noinline)) foo(int x)
{
    return rand();
}

int __attribute__((noinline)) boo(int x)
{
    return rand();
}


int aaa(int x)
{
    int result;

    if(x == 5) 
        result = foo(x);
    else
        result = boo(x);

    return result;
}

int bbb(int x)
{
    int result;

    return (x == 5 ? foo : boo)(x);
}

int (*z[2])(int) = {foo, boo};

int ccc(int x)
{
    return z[!!(x == 5)](x);
}

以及由此产生的代码。

foo:
        jmp     rand
boo:
        jmp     rand
aaa:
        cmp     edi, 5
        je      .L6
        jmp     boo
.L6:
        jmp     foo
bbb:
        cmp     edi, 5
        mov     eax, OFFSET FLAT:foo
        mov     edx, OFFSET FLAT:boo
        cmovne  rax, rdx
        jmp     rax
ccc:
        xor     eax, eax
        cmp     edi, 5
        sete    al
        jmp     [QWORD PTR z[0+rax*8]]
z:
        .quad   foo
        .quad   boo

https:/godbolt.orgzL6CFs9

在我看来,如果你在不那么琐碎的代码中做这样的微优化--你需要看到产生的代码,并决定什么是更有效的。


5
投票

规则#0--不要从原始速度的角度考虑,而是要从 "8个月后当有人报告一个bug时,我宁愿修复哪个 "的角度考虑。

规则#1 - 衡量,不要猜测,也不要让没有权限进入你系统的人去猜测。 在目标系统上编写两个版本的代码,并对它们进行剖析--检查生成的机器代码,并将每个版本与一个足够大的测试集进行对比,以生成可用的统计数据并分析结果。 考虑它是如何使用的--它是在一个紧密的循环中被调用数千次,还是在程序的生命周期中被调用一次? 每个函数都涉及到更新文件系统,这比决定调用哪个函数所花费的时间要多很多数量级 不管 的方法。

规则2--如果你的代码给了你错误的答案,或者做了错误的事情,或者把你的信用卡信息暴露给了全世界,或者如果隔壁房间的人打了个喷嚏就会爆炸,或者没有人(包括你自己)能修复或更新它,那么你的代码有多快都不重要。 正确性代码 第一首先是可读性和可维护性,然后是安全性和可靠性。然后 的速度。 你的大部分显著的速度提升来自于使用正确的算法和数据结构,而不是你对流控结构的选择。

规则#3 - 不要用三元运算符来替代 if-else 架构 只是 的流量控制;这不是它的工作。 虽然第一个版本是有效的,但它有点刺眼,而且很难一目了然,当你六个月后再拿起它时,你会问自己为什么要这么做。 而且我几乎可以保证,它不会比另一种方法快或慢得可以衡量。

我不是说速度不重要--我是说速度只是需要考虑的一件事,除非你在特定的领域工作,否则它不是最重要的事情。


2
投票

非常可以想象,一个优化的编译器会针对两种情况生成samecode。

奇怪的是,在这种情况下,gcc和clang并没有这样做,而是生成了一段代码,从字面上看,它使用函数指针为 :? 和直接跳转到第二种情况。

例子

#include <dirent.h>
#include <stdio.h>
#include <unistd.h>

int rmitem0(struct dirent const*Entry)
{
    return (Entry->d_type == DT_DIR ? rmdir : remove)(Entry->d_name);
}

int rmitem1(struct dirent const*Entry)
{
    if (Entry->d_type == DT_DIR)
        return rmdir(Entry->d_name);
    else return remove(Entry->d_name);
}

x86_64 clang:

0000000000000000 <rmitem0>:
   0:   80 7f 12 04             cmp    BYTE PTR [rdi+0x12],0x4
   4:   b8 00 00 00 00          mov    eax,0x0  5: R_X86_64_32  rmdir
   9:   b9 00 00 00 00          mov    ecx,0x0  a: R_X86_64_32  remove
   e:   48 0f 44 c8             cmove  rcx,rax
  12:   48 83 c7 13             add    rdi,0x13
  16:   ff e1                   jmp    rcx

0000000000000018 <rmitem1>:
  18:   80 7f 12 04             cmp    BYTE PTR [rdi+0x12],0x4
  1c:   48 8d 7f 13             lea    rdi,[rdi+0x13]
  20:   0f 85 00 00 00 00       jne    26 <rmitem1+0xe> 22: R_X86_64_PLT32  remove-0x4
  26:   e9 00 00 00 00          jmp    2b <rmitem1+0x13>    27: R_X86_64_PLT32  rmdir-0x4

x86_64 gcc:

0000000000000000 <rmitem0>:
   0:   80 7f 12 04             cmp    BYTE PTR [rdi+0x12],0x4
   4:   74 09                   je     f <rmitem0+0xf>
   6:   48 8b 05 00 00 00 00    mov    rax,QWORD PTR [rip+0x0]        # d <rmitem0+0xd> 9: R_X86_64_REX_GOTPCRELX   remove-0x4
   d:   eb 07                   jmp    16 <rmitem0+0x16>
   f:   48 8b 05 00 00 00 00    mov    rax,QWORD PTR [rip+0x0]        # 16 <rmitem0+0x16>   12: R_X86_64_REX_GOTPCRELX  rmdir-0x4
  16:   48 83 c7 13             add    rdi,0x13
  1a:   ff e0                   jmp    rax

000000000000001c <rmitem1>:
  1c:   4c 8d 47 13             lea    r8,[rdi+0x13]
  20:   80 7f 12 04             cmp    BYTE PTR [rdi+0x12],0x4
  24:   4c 89 c7                mov    rdi,r8
  27:   75 05                   jne    2e <rmitem1+0x12>
  29:   e9 00 00 00 00          jmp    2e <rmitem1+0x12>    2a: R_X86_64_PLT32  rmdir-0x4
  2e:   e9 00 00 00 00          jmp    33 <rmitem1+0x17>    2f: R_X86_64_PLT32  remove-0x4

因此,这两种策略在这里应该会有稍微不同的性能特征,但无论如何,你都错过了一棵小树的森林。

我已经测量了一个 rmdir 在Linux上约为14µs。

上面的条件应该只需要一个ns的一小部分,最多几个ns:这比你的瓶颈快了10000多倍。

最新问题
© www.soinside.com 2019 - 2024. All rights reserved.