问题描述
我有一个使用数据库的应用程序,其中数据以大端顺序存储。为了跨硬件平台可移植地访问这些数据,我使用了 config.h 模块中定义的 4 个宏:
word(p) - gets a big-endian 16 bit value at pointer p as a native 16-bit value.
putword(p,w) - stores a native 16-bit variable (w) to pointer p as 16-bit big-endian.
dword(p) and putdword(p,d) do the same for 32-bit values
这一切都很好,但小端机器上的宏使用了蛮力的“移位和掩码”方法。
无论如何,看起来 linux 上有 builtin_bswap16 和 builtin_bswap32 函数可以更有效地执行此操作(作为内联汇编代码?)。那么对我的 word/putword 宏进行编码以便它们在 X86_64 linux 机器上使用这些内置函数的正确方法是什么?将我的宏编码为 htons/l 函数调用是否会有效地做同样的事情 - 是否有必要启用编译器优化以使这些解决方案中的任何一个工作?如果它使 gdb 无用,我宁愿不优化。
解决方法
嗯。我编写了一个简单的测试程序,没有使用特殊的包含文件,只是直接调用 __builtin_swap... 函数(请参阅下面的“fast...”宏)。这一切都只是有效。当我在 gdb 中反汇编代码时,我看到 fast... 宏在 4-5 条汇编指令中完成,对于最坏情况的 'dword' 宏,最多需要 27 条指令。几乎不费吹灰之力就进行了相当巧妙的改进。
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
#define word(a) (ushort) ( (*((uchar *)(a)) << 8) | \
(*((uchar *)(a) + 1)) )
#define putword(a,w) *((char *)(a)) = (char) (((ushort)((w) >> 8)) & 0x00ff),\
*((char *)(a)+1) = (char) (((ushort)((w) >> 0)) & 0x00ff)
#define dword(a) (uint) ( ((uint)(word(a)) << 16) | \
((uint)(word(((uchar *)(a) + 2)))) )
#define putdword(a,d) *((char *)(a)) = (char) (((uint)((d) >> 24)) & 0x00ff),\
*((char *)(a)+1) = (char) (((uint)((d) >> 16)) & 0x00ff),\
*((char *)(a)+2) = (char) (((uint)((d) >> 8)) & 0x00ff),\
*((char *)(a)+3) = (char) (((uint)((d) >> 0)) & 0x00ff)
#define fastword(a) (ushort) __builtin_bswap16(* ((ushort *) a));
#define fastputword(a,w) *((ushort *) a) = __builtin_bswap16((ushort)w);
#define fastdword(a) (uint) __builtin_bswap32(* ((uint *) a));
#define fastputdword(a,d) *((uint *) a) = __builtin_bswap32((uint)d);
int main()
{
unsigned short s1,s2,s3;
unsigned int i1,i2,i3;
s1 = 0x1234;
putword(&s2,s1);
s3 = word(&s2);
i1 = 0x12345678;
putdword(&i2,i1);
i3 = dword(&i2);
printf("s1=%x,s2=%x,s3=%x,i1=%x,i2=%x,i3=%x\n",s1,s3,i1,i3);
s1 = 0x1234;
fastputword(&s2,s1);
s3 = fastword(&s2);
i1 = 0x12345678;
fastputdword(&i2,i1);
i3 = fastdword(&i2);
printf("s1=%x,i3);
}
,
我只会使用 htons,htonl
和朋友。它们的可移植性要好得多,而且很可能任何给定 libc 的作者都将它们实现为内联函数或宏,这些函数或宏调用 __builtin
内在函数或内联 asm 或其他任何东西,从而导致几乎 -该特定机器的最佳实现。 See what is generated in godbolt's setup,我认为这是 Linux/glibc 的某种风格。
您确实需要使用优化来编译它们才能被内联,否则它会生成一个普通的函数调用。但即使是 -Og
也让它们内联,也不应该把你的调试弄得一团糟。无论如何,如果您在编译时完全不进行优化,那么您的整个程序将非常低效,以至于调用 htons
的额外指令肯定是您最不担心的。