c - 使用按位运算符进行优化

标签 c embedded bit-manipulation

<分区>

我正在为嵌入式系统编写一个软件,它会在很短的时间内读取很多输入数据(包括频率),因此主循环需要尽可能快。

不过,我无法决定如何实现以下代码。哪个版本的代码运行得更快?编译器会自动优化代码吗? AND 运算是否比赋值运算慢?

第一个代码:

   if ((a&b) == (b&c))
   {
     if (a&b)
       //something;
     else
       //something else;
   }

第二个代码:

 int p;
 if ((p = (a&b)) == (b&c))
   {
     if (p)
       //something;
     else
       //something else;
   }

最佳答案

一般来说应该没什么区别,这取决于你对变量 p 做了什么。在我下面的例子中,p 从未被使用过,因此应该被优化掉,但是 gcc 对此做了一些非常有趣的事情。

unsigned int fun1 ( unsigned int a, unsigned int b, unsigned int c )
{

   if ((a&b) == (b&c))
   {
     if (a&b)
        return(1);
     else
        return(2);
   }

}

unsigned int fun2 ( unsigned int a, unsigned int b, unsigned int c )
{

 int p;
 if ((p = (a&b)) == (b&c))
   {
     if (a&b)
        return(1);
     else
        return(2);
   }

}

首先,根据您的特定 bool 代数进行优化,使用此处理器选择不同的位运算符,您可能看不出有什么不同。

00000000 <fun1>:
   0:   e0222000    eor r2, r2, r0
   4:   e1120001    tst r2, r1
   8:   1a000003    bne 1c <fun1+0x1c>
   c:   e1110000    tst r1, r0
  10:   13a00001    movne   r0, #1
  14:   03a00002    moveq   r0, #2
  18:   e12fff1e    bx  lr
  1c:   e12fff1e    bx  lr

00000020 <fun2>:
  20:   e0010000    and r0, r1, r0
  24:   e0022001    and r2, r2, r1
  28:   e1500002    cmp r0, r2
  2c:   0a000000    beq 34 <fun2+0x14>
  30:   e12fff1e    bx  lr
  34:   e3500000    cmp r0, #0
  38:   13a00001    movne   r0, #1
  3c:   03a00002    moveq   r0, #2
  40:   e12fff1e    bx  lr

因此,在您为 p 赋值的地方,它正在准备 r0 以保存该值,即使它从未被使用过。很奇怪编译器没有捕捉到。因为我没有指定返回值,所以您可以使用上面的 fun2 代码返回 p。如果您在末尾添加一个返回值,那么编译器会简单地将其添加到上述两个函数中。编译器应该也提示我没有返回值并且没有。

对于 fun1() 它似乎是在使用快捷方式来决定是否进入顶层,然后从那里开始。 Fun2 正在生成 p 变量,然后在编写 C 代码时使用它(比较 ands)。对于 fun2() 的这个实现,您将刻录一条额外的指令以使其速度变慢。如果不是 xor 快捷方式,使用这个处理器,如果它执行了两个 ands,执行时间将是相同的,编译器可以简单地决定将其中一个寄存器保留为 p 以备后用,或者只是丢弃寄存器.因此,如果您使用不同的按位运算符,您会期望两种方式的代码速度相同。

使用 llvm 而不是 gcc,还要注意我在底部添加了返回值:

unsigned int fun1 ( unsigned int a, unsigned int b, unsigned int c )
{

   if ((a&b) == (b&c))
   {
     if (a&b)
        return(1);
     else
        return(2);
   }
   return(3);

}

unsigned int fun2 ( unsigned int a, unsigned int b, unsigned int c )
{

 int p;
 if ((p = (a&b)) == (b&c))
   {
     if (a&b)
        return(1);
     else
        return(2);
   }
 return(3);
}

在获取特定处理器之前(注意这是前端的 clang)

define i32 @fun1(i32 %a, i32 %b, i32 %c) nounwind readnone {
  %1 = xor i32 %c, %a
  %2 = and i32 %1, %b
  %3 = icmp eq i32 %2, 0
  br i1 %3, label %4, label %8

; <label>:4                                       ; preds = %0
  %5 = and i32 %b, %a
  %6 = icmp eq i32 %5, 0
  br i1 %6, label %7, label %8

; <label>:7                                       ; preds = %4
  br label %8

; <label>:8                                       ; preds = %7, %4, %0
  %9 = phi i32 [ 2, %7 ], [ 1, %4 ], [ 3, %0 ]
  ret i32 %9
}

define i32 @fun2(i32 %a, i32 %b, i32 %c) nounwind readnone {
  %1 = xor i32 %c, %a
  %2 = and i32 %1, %b
  %3 = icmp eq i32 %2, 0
  br i1 %3, label %4, label %8

; <label>:4                                       ; preds = %0
  %5 = and i32 %b, %a
  %6 = icmp eq i32 %5, 0
  br i1 %6, label %7, label %8

; <label>:7                                       ; preds = %4
  br label %8

; <label>:8                                       ; preds = %7, %4, %0
  %9 = phi i32 [ 2, %7 ], [ 1, %4 ], [ 3, %0 ]
  ret i32 %9
}

它优化了 p 变量,因为它没有被使用...

00000000 <fun1>:
   0:   e1a03000    mov r3, r0
   4:   e3a00003    mov r0, #3
   8:   e0222003    eor r2, r2, r3
   c:   e1120001    tst r2, r1
  10:   11a0f00e    movne   pc, lr
  14:   e3a00001    mov r0, #1
  18:   e1110003    tst r1, r3
  1c:   03a00002    moveq   r0, #2
  20:   e1a0f00e    mov pc, lr

00000024 <fun2>:
  24:   e1a03000    mov r3, r0
  28:   e3a00003    mov r0, #3
  2c:   e0222003    eor r2, r2, r3
  30:   e1120001    tst r2, r1
  34:   11a0f00e    movne   pc, lr
  38:   e3a00001    mov r0, #1
  3c:   e1110003    tst r1, r3
  40:   03a00002    moveq   r0, #2
  44:   e1a0f00e    mov pc, lr

为两个函数提供相同的代码。他们进行了异或测试,而不是进行两个与运算。

我通常希望有一条额外的指令来保留第二个函数中的值,具体取决于处理器、优化以及您对该值的处理方式。相对于实现比较的其余代码,或者当您添加堆栈/内存访问以保留该值时,影响可能不小,可能会花费您 50% 或更多的时间。

出于好奇我也尝试了这个

unsigned int fun3 ( unsigned int a, unsigned int b, unsigned int c )
{

 int p;
 p = a&b;
 if (p == (b&c))
   {
     if (p)
        return(1);
     else
        return(2);
   }
 return(3);
}

海湾合作委员会:

0000004c <fun3>:
  4c:   e0010000    and r0, r1, r0
  50:   e0022001    and r2, r2, r1
  54:   e1500002    cmp r0, r2
  58:   0a000001    beq 64 <fun3+0x18>
  5c:   e3a00003    mov r0, #3
  60:   e12fff1e    bx  lr
  64:   e3500000    cmp r0, #0
  68:   03a00002    moveq   r0, #2
  6c:   13a00001    movne   r0, #1
  70:   e12fff1e    bx  lr

LLVM

00000048 <fun3>:
  48:   e0013000    and r3, r1, r0
  4c:   e0021001    and r1, r2, r1
  50:   e3a00003    mov r0, #3
  54:   e1530001    cmp r3, r1
  58:   11a0f00e    movne   pc, lr
  5c:   e3a00001    mov r0, #1
  60:   e3530000    cmp r3, #0
  64:   03a00002    moveq   r0, #2
  68:   e1a0f00e    mov pc, lr

gcc 正在燃烧一个分支及其成本,而 llvm 正在采用流水线方法燃烧一些额外的指令周期,但节省了分支和管道刷新。

你开始明白了吗?您的问题非常针对处理器和编译器以及编译选项和系统(内存周期成本与指令周期成本等)。我希望性能与合理速度的内存接口(interface)相同或慢 15% 到 200%。

如果您担心这几行代码的速度...用汇编语言编写...

关于c - 使用按位运算符进行优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8479453/

相关文章:

c - 为什么这个 C 函数什么都不做?

vs2010 c 找不到 dll

debugging - 通过 Cortex-M3 CPU 上的 printf 进行输出调试,在 BKPT 指令处停顿 + 关于 JTAG 和 sw 端口的混淆

c++ - beagle bone black 的 undefined reference 链接器错误静态库交叉编译

c - 使用 libnl-3 发送 Netlink Taskstats 消息

c - 将套接字传递给线程而不是 fd?

c - C 中的线程(和 irq)安全动态内存处理程序

vb.net - 如何在VB.NET中按位转换?

algorithm - 基于 IEEE 754 手动将十进制浮点转换为位表示的最简单方法,无需使用任何库

c - 使用位级和逻辑运算编写 x == y 的等价内容?