重写没有分支的代码

时间:2015-06-24 17:31:46

标签: c optimization bit-manipulation inline

我有以下功能:

uint16_t foo(uint8_t input)
{
     uint16_t N = 38;
     if (!(input&1))
     {
       N = 0;
     }
     else 
     {
        if ((input&2) >> 1)
        {
           N = ~N;
        }
     }
     return N;
}

我希望在没有ifs的情况下重写它,就像内联函数一样,将38转换为0,3865497给定input和仅使用标准C比特操作。

关键不在于编译器可以内联函数,或者函数是快速的,而只是为了摆脱分支并保持恒定时间而不管input是什么。

第一个if很简单:

uint16_t c = ((input&1)^1)-1;
N &= c;

但是我找到一些简单的方法去做条件否定时遇到了麻烦。

5 个答案:

答案 0 :(得分:5)

使用表格查找。

uint16_t foo(uint8_t input) {
    int index= input & 0x3;
    const uint16 table[4]= {0,~38,0,38};
    return table[index];
}

答案 1 :(得分:3)

要保证运行时和平衡代码,必须进行汇编。由于MSP430汇编器并不复杂,因此不会出现太大问题。请注意,乘法等操作可能由常量运行时的函数执行。

避免分支是没有意义的。相反,您应该平衡执行路径,例如使用NOP(无操作)。 MSP430用户指南包括指令时序。请注意,时序是固定的,这与ARM等较大的CPU不同,后者依赖于流水线和内存时序。

一般说明:通过CPU周期进行计时通常是一个坏主意。像MSP430这样的现代MCU提供内部定时器和与其他外设(如ADC)的连接,以触发例如转换具有高度准确的时间。它们可以生成中断,因此CPU可以准备下一个值或读取样本而无需关心代码的运行时间(除非它花费的时间太长)。

使用CPU来禁止例如中断,因为这会打击任何时间。这使得维护这样一个系统成为一场噩梦。

答案 2 :(得分:2)

如果我以正确的方式阅读您的代码,则说明如下:

uint16_t foo1(uint8_t input)
{
     uint16_t N = 38;

     uint16_t bit0 = input & 1;
     uint16_t nbit0 = bit0 ^ 1;
     uint16_t bit1 = (input & 2) >> 1;
     uint16_t nbit1 = bit1 ^ 1;

     N = nbit0 * ( bit1 * ~N + nbit1 * N);

     return N;
}

随意摆脱变量。它们只是为了便于阅读。

答案 3 :(得分:2)

这应该有用。

#include<stdio.h>

int main(){
    int input;
    scanf("%d", &input);
    int N = (input&1)*(((input&2)*32729)+(38+((input&2)>>1)));
    printf("%d\n", N);
}

答案 4 :(得分:2)

7次操作,没有乘法,建立在njuffa的评论之上:

uint16_t bar(uint8_t input)
{
    return (0-(input&1)) & (38^(0-((input>>1)&1)));
}

Demo