c - 为什么 C 编译器优化 switch 和 if 不同

标签 c gcc optimization bit-manipulation disassembly

我最近在做一个个人项目时偶然发现了一个奇怪的问题。

在一个非常紧凑的循环中,我有一个整数,其值介于 0 和 15 之间。我需要为值 0、1、8 和 9 获取 -1,为值 4、5、12 和 13 获取 1。

我求助于 Godbolt 检查了几个选项,令我惊讶的是编译器似乎无法像优化 if 链一样优化 switch 语句。

链接在这里:https://godbolt.org/z/WYVBFl

代码是:

const int lookup[16] = {-1, -1, 0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1, 0, 0};

int a(int num) {
    return lookup[num & 0xF];
}

int b(int num) {
    num &= 0xF;

    if (num == 0 || num == 1 || num == 8 || num == 9) 
        return -1;

    if (num == 4 || num == 5 || num == 12 || num == 13)
        return 1;

    return 0;
}

int c(int num) {
    num &= 0xF;
    switch (num) {
        case 0: case 1: case 8: case 9: 
            return -1;
        case 4: case 5: case 12: case 13:
            return 1;
        default:
            return 0;
    }
}

我原以为 b 和 c 会产生相同的结果,并且我希望我可以阅读 bit-hacks 以自己提出一个有效的实现,因为我的解决方案(switch 语句 - 另一种形式)是相当慢。

奇怪的是,b 编译为 bit-hacks 而 c 要么几乎未优化,要么简化为不同的 a 情况,具体取决于在目标硬件上。

谁能解释为什么会出现这种差异?优化此查询的“正确”方法是什么?

编辑:

澄清

希望 switch 解决方案是最快的,或者类似的“干净”解决方案。然而,当在我的机器上进行优化编译时,if 解决方案明显更快。

我编写了一个快速程序来演示,TIO 的结果与我在本地找到的结果相同:Try it online!

使用 static inline 查找表会加快一点:Try it online!

最佳答案

如果你明确地枚举所有的情况,gcc 是非常有效的:

int c(int num) {
    num &= 0xF;
    switch (num) {
        case 0: case 1: case 8: case 9: 
            return -1;
        case 4: case 5: case 12: case 13:
            return 1;
            case 2: case 3: case 6: case 7: case 10: case 11: case 14: case 15: 
        //default:
            return 0;
    }
}

只是在一个简单的索引分支中编译:

c:
        and     edi, 15
        jmp     [QWORD PTR .L10[0+rdi*8]]
.L10:
        .quad   .L12
        .quad   .L12
        .quad   .L9
        .quad   .L9
        .quad   .L11
        .quad   .L11
        .quad   .L9
        .quad   .L9
        .quad   .L12
etc...

请注意,如果 default: 未被注释,gcc 将返回其嵌套分支版本。

关于c - 为什么 C 编译器优化 switch 和 if 不同,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58332098/

相关文章:

c - 从 C 中的文件创建链接列表时发生内存访问冲突

c - 有没有办法获得具有 AVX 支持的 Tensorflow 的 C API?

Linux X-Loader 起始地址位于 config.mk 中而不是链接描述文件中

multithreading - 这里需要 volatile 吗?

Mysql:什么是碎片表,为什么要对它们运行 OPTIMIZE?

python - 如何将参数传递给 scipy.optimize 中的非线性约束?

memory - 统一内存与固定主机内存的行为和性能

c - HDF5:更新整数表中的单元格

c - 声明结构以避免混合声明和代码

c - Strace 与 C 可执行文件?