首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用C语言减少if语句以提高效率

用C语言减少if语句以提高效率
EN

Stack Overflow用户
提问于 2018-02-24 22:38:45
回答 3查看 1.5K关注 0票数 2

我试图通过减少条件语句的数量来提高代码的效率,因为将来我将在程序集中编写代码。你们认为有什么方法可以减少这些陈述以提高效率吗?

代码语言:javascript
复制
if (j==18)
{
    store=12;
}
else if(j==30)
{
    store=10;
}
else if(j==42)
{
    store=8;
}
else if(j==54)
{
    store=6;
}
else if(j==66)
{
    store=4;
}
else if(j==78)
{
    store=2;
}
else if(j==92)
{
    store=2;
}
else if(j==106)
{
    store=4;
}
else if(j==120)
{
    store=6;
}
else if(j==134)
{
    store=8;
}
else if(j==148)
{
    store=10;
}
else if(j==162)
{
    store=12;
}
else store=1;
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2018-02-24 22:46:12

当然,使用switch语句比使用一系列if语句更快(而且更紧凑)。大多数语言可以优化一个开关语句,比一系列的ifs要好得多。您的代码在switch语句形式中如下所示:

代码语言:javascript
复制
switch(j) {
    case 18:
        store = 12;
        break;
    case 30:
        store = 10;
        break;
    // and so on
    default:
        store = 1;
}

当然,这不会像没有条件的代码版本那么快(可能)。如果你能想出一个数学公式,用j来计算j的值,那(可能)要快得多。

票数 4
EN

Stack Overflow用户

发布于 2018-02-24 23:38:49

代码语言:javascript
复制
if (j < 18 || 162 < j) {
    store = 1;
} else if (j < 90) {
    int mod12 = (j-6) % 12;
    // ((j-6)/12) -> 18=>1, .. 78=>6  (/6 used to get *2)
    store = (mod12) ? 1 : 14 - ((j-6) / 6);
} else {
    int mod14 = (j-8) % 14;
    // ((j-8)/14) -> 92=>6, ... 162=>11 (/7 used to get *2)
    store = (mod14) ? 1 : ((j-8) / 7) - 10;
}

这可以通过手动汇编程序直接实现,尽管现代C++编译器会比普通的人做得更好,例如,gcc 7.3制作了一些更好的东西比我最初想到的要好(还有一些我不想手工编写的东西)。

其实gcc可以是用手握住一点,更好地理解这个公式。

修改后的来源:

代码语言:javascript
复制
if (j < 18 || 162 < j) {
    store = 1;
} else if (j < 90) {
    int mod12 = (j-6) % 12;
    // ((j-6)/12) -> 18=>1, .. 78=>6
    store = (mod12) ? 1 : 14 - 2*((j-6) / 12);
} else {
    int mod14 = (j-8) % 14;
    // ((j-8)/14) -> 92=>6, ... 162=>11
    store = (mod14) ? 1 : 2*((j-8) / 14) - 10;
}

为了完整起见,下面是switch版本(没有对其进行基准测试,但应该比上面的代码慢得多):https://godbolt.org/g/ELNCYD

看来gcc没能弄明白,他确实用了很多“如果”。

:因此,在检查所有编译器/和注释之后,这看起来是性能最优的x86_64解决方案(子例程在edi中使用"j“,在rax中返回”存储“)(edi语法):

代码语言:javascript
复制
; input: edi = j, output: rax = store
storeByJ:
    sub     edi, 18
    cmp     edi, (162-18)
    ja      .JoutOfRange    ; j < 18 || 162 < j -> return 1
    ; rdi = 0 .. 162-18 = 144
    movzx   eax, byte [rel .JtoStoreLUT + rdi]
    ret
.JoutOfRange:
    mov     eax,1
    ret

.JtoStoreLUT:
    ;        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F
    db      12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1         ; 18->12
    db      10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1         ; 30->10
    db       8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1         ; 42->8
    db       6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1         ; 54->6
    db       4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1         ; 66->4
    db       2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ; 78->2
    db       2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ; 92->2
    db       4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ;106->4
    db       6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ;120->6
    db       8, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ;134->8
    db      10, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1   ;148->10
    db      12                                          ;162->12

如果您有更大的范围(更多的值)仍然是+12,并且在某个点+14被区分之后,这个公式可能会变得更好(通过从太大的LUT (查找表)中保存缓存内存),但是此时,即使是LUT数据,这段代码也大约有170 B长,所以它很可能适合使用它的整个循环。

编辑:另一个有一半大小LUT的变体,但使用ror通过范围测试中的一次跳转过滤出奇数值(我不确定性能,但就像任何其他代码高效问题一样,分析绝对是必不可少的,首先是理论推理,如果你不能在基准测试中证实你的理论,修正你的基准(很可能),或者找出令人惊讶的复杂的CPU内部结构,以及你是如何误解某些东西的……(但这种情况仍有可能经常发生)使用cmovCC (总计97B)消除范围分支:

代码语言:javascript
复制
; input: edi = j, output: eax = store
storeByJ:
    mov     eax, 1
    sub     edi, 18
    ror     edi, 1          ; /2 but keeps "odd" bit in the edi
                            ; to make it fail range check on next line
    cmp     edi, (162-18)/2
    cmova   edi, eax        ; j < 18 || 162 < j || j&1 -> return 1 (from LUT[1])
    ; rdi = 0 .. (162-18)/2 = 72  # rdi = (j-18)/2
    movzx   eax, byte [rel .JtoStoreLUT + rdi]
    ret

.JtoStoreLUT:
    ;        0, 1, 2, 3, 4, 5, 6
    db      12, 1, 1, 1, 1, 1       ;  18->12
    db      10, 1, 1, 1, 1, 1       ;  30->10
    db       8, 1, 1, 1, 1, 1       ;  42->8
    db       6, 1, 1, 1, 1, 1       ;  54->6
    db       4, 1, 1, 1, 1, 1       ;  66->4
    db       2, 1, 1, 1, 1, 1, 1    ;  78->2
    db       2, 1, 1, 1, 1, 1, 1    ;  92->2
    db       4, 1, 1, 1, 1, 1, 1    ; 106->2
    db       6, 1, 1, 1, 1, 1, 1    ; 120->2
    db       8, 1, 1, 1, 1, 1, 1    ; 134->2
    db      10, 1, 1, 1, 1, 1, 1    ; 148->2
    db      12                      ; 162->2
票数 5
EN

Stack Overflow用户

发布于 2018-02-24 23:15:16

如果这些都是您必须处理的j,我将尝试使用数组162、char甚至更少。这听起来是一种内存浪费,但考虑到所有节省的指令,它们也占用了内存。M2c

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/48968358

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档