有人要求我改变一个整数的内在性。我当时的想法是使用移位
int swap_endianess(int color)
{
int a;
int r;
int g;
int b;
a = (color & (255 << 24)) >> 24;
r = (color & (255 << 16)) >> 16;
g = (color & (255 << 8)) >> 8;
b = (color & 255)
return (b << 24 | g << 16 | r << 8 | a);
}
但是有人告诉我,使用包含一个int和四个字符的数组的联合会更容易(如果一个int存储在4个字符上),填充int然后反转该数组。
union u_color
{
int color;
char c[4];
};
int swap_endianess(int color)
{
union u_color ucol;
char tmp;
ucol.color = color;
tmp = ucol.c[0];
ucol.c[0] = ucol.c[3];
ucol.c[3] = tmp;
tmp = ucol.c[1];
ucol.c[1] = ucol.c[2];
ucol.c[2] = tmp;
return (ucol.color);
}
在这两者之间交换字节的更有效方法是什么?有更有效的方法吗?
在I7上进行测试后,联合方式大约需要24秒(用time
命令测量),而移位方式在2,000,000,000次迭代中大约需要15秒。如果我使用-O1进行编译,则这两种方法仅需1秒,而使用-O2或-O3只需0.001秒。
位偏移方法bswap
在ASM中使用-02和-03编译,但不是联合方式,gcc似乎可以识别幼稚的模式,但不能识别复杂的联合方式。最后,请阅读@ user3386109的底行。
这是字节交换功能的正确代码
uint32_t changeEndianess( uint32_t value )
{
uint32_t r, g, b, a;
r = (value >> 24) & 0xff;
g = (value >> 16) & 0xff;
b = (value >> 8) & 0xff;
a = value & 0xff;
return (a << 24) | (b << 16) | (g << 8) | r;
}
这是一个测试字节交换功能的功能
void testEndianess( void )
{
uint32_t value = arc4random();
uint32_t result = changeEndianess( value );
printf( "%08x %08x\n", value, result );
}
通过全面优化使用LLVM编译器,该testEndianess
函数的最终汇编代码为
0x93d0: calll 0xc82e ; call `arc4random`
0x93d5: movl %eax, %ecx ; copy `value` into register CX
0x93d7: bswapl %ecx ; <--- this is the `changeEndianess` function
0x93d9: movl %ecx, 0x8(%esp) ; put 'result' on the stack
0x93dd: movl %eax, 0x4(%esp) ; put 'value' on the stack
0x93e1: leal 0x6536(%esi), %eax ; compute address of the format string
0x93e7: movl %eax, (%esp) ; put the format string on the stack
0x93ea: calll 0xc864 ; call 'printf'
换句话说,LLVM编译器识别整个changeEndianess
功能并将其实现为单个bswapl
指令。
对于那些想知道为什么需要调用的人的旁注arc4random
。给定此代码
void testEndianess( void )
{
uint32_t value = 0x11223344;
uint32_t result = changeEndianess( value );
printf( "%08x %08x\n", value, result );
}
编译器生成该程序集
0x93dc: leal 0x6524(%eax), %eax ; compute address of format string
0x93e2: movl %eax, (%esp) ; put the format string on the stack
0x93e5: movl $0x44332211, 0x8(%esp) ; put 'result' on the stack
0x93ed: movl $0x11223344, 0x4(%esp) ; put 'value' on the stack
0x93f5: calll 0xc868 ; call 'printf'
换句话说,给定一个硬编码value
作为输入,编译器的预计算result
的的changeEndianess
功能,并提出其直接进入汇编代码,完全绕过功能。
底线。以编写代码的合理方式编写代码,然后让编译器进行优化。这些天的编译器很棒。在源代码中使用棘手的优化(例如,并集)可能会破坏编译器中内置的优化,从而导致代码变慢。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句