繁体   English   中英

减少C中的语句以提高效率

[英]reducing if statements in C for efficiency

我试图通过减少条件语句的数量来提高我的代码效率,因为我将来会在汇编中编写代码。 你们有没有办法减少陈述的效率?

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;
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可以手工掌握,以便更好地理解该公式

修改来源:

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无法弄清楚它并且确实使用了很多“ifs”。


:所以在检查所有那些编译器/和注释之后,这看起来是最好的[性能]最佳x86_64解决方案( edi “j”子程序并在rax返回“store”)(NASM语法):

; 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(Look-Up-Table)保存缓存),但是此时此代码即使使用LUT数据也长约170B,因此即使使用整个循环,它也很可能适合。


编辑:另一个变体,具有半大小的LUT,但是在范围测试中使用ror通过单跳过滤掉奇数值(我不确定性能,但与任何其他代码有效的问题一样,分析是绝对必要的,首先是理论推理,如果你不能在基准测试中确认你的理论,修复你的基准测试(更有可能),或者发现CPU内部的惊人错综复杂以及你如何误解某些东西......(但这仍然可能足以经常发生))..并使用cmovCC (总计97B)消除范围分支:

; 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

好吧,使用switch语句而不是一系列if语句肯定会更快(更紧凑)。 大多数语言都可以比一系列ifs更好地优化switch语句。 您的代码在switch语句中看起来像这样:

switch(j) {
    case 18:
        store = 12;
        break;
    case 30:
        store = 10;
        break;
    // and so on
    default:
        store = 1;
}

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

我会尝试使用数组162 char或更少,如果这些都是你必须处理的j。 这听起来像是一种记忆浪费,但考虑到所有这些指令都被保存了,它们也占据了记忆。 M2C

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM