[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] maint: Use logical rather than bitwise operators on bools
From: |
Pádraig Brady |
Subject: |
Re: [PATCH] maint: Use logical rather than bitwise operators on bools |
Date: |
Wed, 23 Sep 2009 21:11:37 +0100 |
User-agent: |
Thunderbird 2.0.0.6 (X11/20071008) |
Paul Eggert wrote:
> Pádraig Brady <address@hidden> writes:
>
>> As well as being distracting, bitwise operators are
>> not short circuiting and brittle with C89 where
>> bool can be an int, i.e. > 1.
>
> I don't find them distracting; on the contrary, I find them a helpful
> reminder that the expressions are boolean (in the traditional
> mathematical sense) and so I don't need to worry about the
> complexities of short-circuit evaluation.
>
> Also, the conditional branches generated by short-circuit operators
> can make code harder to debug because GDB starts jumping all over the
> place when I single step.
>
> Furthermore, bitwise operators often generate shorter and faster code,
> particularly on modern processors where conditional branches are far
> more expensive than logical operations. For example, consider these
> functions:
>
> bool F (bool a, bool b, bool c, bool d) { return a && b && c && d; }
> bool G (bool a, bool b, bool c, bool d) { return a & b & c & d; }
Well I did actually compare the assembly for chcon.c which
was the first I noticed, before I decided it might be worth doing:
$ objdump -S chcon.o > chcon.logical #and likewise for bitwise
$ sdiff chcon.bitwise chcon.logical
...
if (recurse & preserve_root) | if (recurse &&
preserve_root)
6c0: 0f b6 05 11 00 00 00 movzbl 0x11,%eax | 6c0:
80 3d 11 00 00 00 00 cmpb $0x0,0x11
6c7: 0f b6 4c 24 34 movzbl 0x34(%esp),%ec | 6c7:
74 0b je 6d4 <main+0x3c
6cc: 85 c8 test %ecx,%eax | 6c9:
80 7c 24 34 00 cmpb $0x0,0x34(%esp
6ce: 0f 85 8b 07 00 00 jne e5f <main+0xb4 | 6ce:
0f 85 3e 0a 00 00 jne 1112 <main+0xe
error (EXIT_FAILURE, errno, _("failed to get attribut error
(EXIT_FAILURE, errno, _("failed to get attribut
You're right about the extra branch but for this common case at least
the size was the same. I did also check the speed with a test program
(attached) which showed the logical test was a bit faster on my pentium-m.
Note I compiled without optimization like `gcc -march=pentium-m chcon-test.c`
so that the loop was actually run, and I got these (averaged) results:
$ time ./bitwise
real 0m4.550s
$ time ./logical
real 0m4.173s
> For F, gcc -O2 (x86) currently generates 3 conditional branches
> (ouch!); for G it generates straightline code (good with pipelining).
> F is also quite a bit bigger than G (20 instructions vs 12; and 49
> bytes versus 30), and this size increase bloats the code and causes
> more instruction cache misses.
>
> Bitwise operators are equivalent in behavior to short-circuit
> operations on boolean expressions (i.e., expressions involving just 0
> and 1) that do not have side effects or conditionally-undefined
> behavior. This is true regardless of whether the boolean type is
> implemented via 'int'. Let's just leave them alone.
Well lots (most?) of the code already uses logical operators
and the mixing of both was confusing me a bit (I'm easily confused :))
I also noticed in expr.c that the null() function was not
short circuited which I changed in the hunk below.
As well as this probably being a lower performance, I needed
to examine the function to know if it had side effects which
one can discount when logical operators are used.
I'm worried about stuff like that creeping in if bitwise
operators are left in place.
diff --git a/src/expr.c b/src/expr.c
index aab09a1..88e0b47 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -923,7 +923,7 @@ eval1 (bool evaluate)
{
if (nextarg ("&"))
{
- r = eval2 (evaluate & ~ null (l));
+ r = eval2 (evaluate && !null (l));
if (null (l) || null (r))
{
freev (l);
@@ -954,7 +954,7 @@ eval (bool evaluate)
{
if (nextarg ("|"))
{
- r = eval1 (evaluate & null (l));
+ r = eval1 (evaluate && null (l));
if (null (l))
{
freev (l);
cheers,
Pádraig.
#include <stdbool.h>
#include <stdio.h>
int main(int argc, char** argv)
{
bool recurse=false;
bool preserve_root=true;
int i;
for (i=0; i<1000000000; i++)
if (recurse && preserve_root)
puts("true");
}