Re: Hex to NSString or NSData
Re: Hex to NSString or NSData
- Subject: Re: Hex to NSString or NSData
- From: Gwynne Raskind <email@hidden>
- Date: Sat, 9 May 2009 19:05:28 -0400
On May 9, 2009, at 5:20 PM, Marcel Weiher wrote:
int hexDigitToInt(char d)
{
int result;
switch (d) {
case '0': result = 0; break;
case '1': result = 1; break;
[snip]
case 'E': result = 14; break;
case 'F': result = 15; break;
default:
result = 0xFF;
}
return result;
}
Although it might look ugly, this is one of the fasted methods
using standard C that converts a hex digit (nibble) to an int (2
machine instructions).
OK, how do you get it to compile to just two instructions? I've
been looking at various optimization options, and as far as I can
tell it is at least doing the following (non-contiguous):
00001e67 subl $0x30,êx
00001e6d cmpl $0x36,êx
00001e70 jal 0x00001f67
00001e76 movl 0x00000038(ëx,êx,4),êx
00001e7d addl ëx,êx
00001e7f jmp *êx
So that's 6 instructions to set up and perform the indexed jump to
which the switch/case is being compiled.
At the jump sites, typically another 2 instructions:
00001f60 movl $0x0000000f,êx
00001f65 jmp 0x00001fd2
So that's a total of 8 instructions, with 2 taken and one typically
non-taken jump and one of the taken jumps computed, so probably not
predictable. I must be missing something obvious.
The smallest (and likely fastest) would probably be the lookup table
version:
char hexCharToNibbleL(char nibble)
{
const char lt[255] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01,
0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
return lt[nibble];
}
This compiles down to (gcc 4.0, -Os, i386 arch):
_hexCharToNibbleL:
00001b6e pushl ëp
00001b6f calll 0x00001b74
00001b74 popl ìx
00001b75 movl %esp,ëp
00001b77 subl $0x08,%esp
00001b7a movsbl 0x08(ëp),êx
00001b7e movsbl 0x0000038c(ìx,êx),êx
00001b86 leave
00001b87 ret
My original if-elseif-elseif version, minus the exception raising,
compiles down to:
_hexCharToNibble:
0000282d pushl ëp
0000282e movl %esp,ëp
00002830 movzbl 0x08(ëp),íx
00002834 leal 0xd0(íx),êx
00002837 cmpb $0x09,%al
00002839 jbe 0x00002856
0000283b leal 0xbf(íx),êx
0000283e cmpb $0x05,%al
00002840 ja 0x00002847
00002842 leal 0xc9(íx),êx
00002845 jmp 0x00002856
00002847 leal 0x9f(íx),êx
0000284a movl $0xffffffff,ìx
0000284f cmpb $0x05,%al
00002851 ja 0x0000285b
00002853 leal 0xa9(íx),êx
00002856 movl êx,ìx
00002858 andl $0x0f,ìx
0000285b leave
0000285c movl ìx,êx
0000285e ret
And the switch-case version compiles to three pages of assembly full
of addls, addbs, hlts, jmps, and so forth.
I ran some (very) simple benchmarks (full source code at the end of
this message).
Examination of the assembly GCC produced for the code shows that gcc
inlined the functions inside the for loops when optimization was
turned on (both -O3 and -Os). The results are at the end of this
message, after the source code.
Some conclusions:
- Unoptimized, all three versions perform more or less the same:
very slowly. This was pretty much a guarantee due to the overhead of
all those uninlined function calls. But I tried forcing GCC to inline
the functions, but it didn't help much, if at all. Conclusion: GCC
produces VERY inefficent code by default.
- Optimized, the lookup table is always the fastest by a factor of
about two
- Optimized 64-bit code is slower in GCC 4.0
- Optimized 64-bit code is faster in GCC 4.2
- GCC 4.2's optimizer is noticably better than GCC 4.0's.
Food for thought :).
-- Gwynne, Daughter of the Code
"This whole world is an asylum for the incurable."
char hexCharToNibbleS(char nibble)
{
int result = 0xFF;
switch (nibble)
{
case '0': result = 0; break;
case '1': result = 1; break;
case '2': result = 2; break;
case '3': result = 3; break;
case '4': result = 4; break;
case '5': result = 5; break;
case '6': result = 6; break;
case '7': result = 7; break;
case '8': result = 8; break;
case '9': result = 9; break;
case 'A': case 'a': result = 10; break;
case 'B': case 'b': result = 11; break;
case 'C': case 'c': result = 12; break;
case 'D': case 'd': result = 13; break;
case 'E': case 'e': result = 14; break;
case 'F': case 'f': result = 15; break;
}
return result;
}
char hexCharToNibbleL(char nibble)
{
const char lt[255] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x01, 0x02,
0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
return *(lt + nibble);
}
char hexCharToNibble(char nibble)
{
// 0 - 9
if (nibble >= '0' && nibble <= '9')
return (nibble - '0') & 0x0F;
// A - F
else if (nibble >= 'A' && nibble <= 'F')
return (nibble - 'A' + 10) & 0x0F;
// a - f
else if (nibble >= 'a' && nibble <= 'f')
return (nibble - 'a' + 10) & 0x0F;
return 0xFF;
}
int main(int argc, char **argv)
{
NSAutoreleasePool *p = [[NSAutoreleasePool alloc] init];
volatile char c = 0;
NSDate *startIF = [NSDate date];
for (NSUInteger i = 0; i < 100000000; ++i)
{
c = hexCharToNibble(i % 255);
}
NSDate *endIF = [NSDate date];
NSDate *startS = [NSDate date];
for (NSUInteger i = 0; i < 100000000; ++i)
{
c = hexCharToNibbleS(i % 255);
}
NSDate *endS = [NSDate date];
NSDate *startL = [NSDate date];
for (NSUInteger i =0; i < 100000000; ++i)
{
c = hexCharToNibbleL(i % 255);
}
NSDate *endL = [NSDate date];
printf("If-else version: %.5f\n", [endIF
timeIntervalSinceDate:startIF]);
printf("Switch version: %.5f\n", [endS
timeIntervalSinceDate:startS]);
printf("Lookup version: %.5f\n", [endL
timeIntervalSinceDate:startL]);
[p release];
return 0;
}
Results:
GCC 4.0, 32-bit, -O0:
If-else version: 2.03375
Switch version: 2.66452
Lookup version: 2.22725
GCC 4.0, 32-bit, -O3:
If-else version: 0.64217
Switch version: 0.60569
Lookup version: 0.41606
GCC 4.0, 32-bit, -Os:
If-else version: 0.70390
Switch version: 0.78775
Lookup version: 0.36639
GCC 4.0, 64-bit, -O0:
If-else version: 4.47304
Switch version: 4.73059
Lookup version: 4.28500
GCC 4.0, 64-bit, -O3:
If-else version: 0.94318
Switch version: 0.99140
Lookup version: 0.50790
GCC 4.0, 64-bit, -Os:
If-else version: 0.95291
Switch version: 0.88173
Lookup version: 0.59685
GCC 4.2, 32-bit, -O0:
If-else version: 2.57359
Switch version: 2.74970
Lookup version: 2.23874
GCC 4.2, 32-bit, -O3:
If-else version: 0.54521
Switch version: 0.59065
Lookup version: 0.42472
GCC 4.2, 32-bit, -Os:
If-else version: 0.71297
Switch version: 0.71112
Lookup version: 0.40114
GCC 4.2, 64-bit, -O0:
If-else version: 4.43196
Switch version: 4.70583
Lookup version: 4.06029
GCC 4.2, 64-bit, -O3:
If-else version: 0.74516
Switch version: 0.70652
Lookup version: 0.33434
GCC 4.2, 64-bit, -Os:
If-else version: 0.81226
Switch version: 0.66370
Lookup version: 0.33090
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden