Linux 访问内置bswap功能的正确方式是什么?

Linux 访问内置bswap功能的正确方式是什么?,linux,gcc,endianness,Linux,Gcc,Endianness,我有一个应用程序,它使用一个数据库,数据以大端顺序存储。为了跨硬件平台可移植地访问这些数据,我使用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 pu

我有一个应用程序,它使用一个数据库,数据以大端顺序存储。为了跨硬件平台可移植地访问这些数据,我使用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
这一切都很好,但在一台小型endian机器上的宏使用蛮力“移位和掩码”方法


无论如何,看起来linux上有内置的bswap16和bswap32函数可以更有效地完成这项工作(作为内联汇编代码?)。那么,编写word/putword宏以便在X86_64 linux机器上使用这些内置函数的正确方法是什么?将我的宏编码为htons/l函数调用会有效地完成同样的事情吗?是否有必要启用编译器优化以使这些解决方案中的任何一个正常工作?如果gdb变得无用,我宁愿不进行优化。

Hmmm。我编写了一个简单的测试程序,不使用特殊的include文件,只需调用uu内置交换。。。直接执行函数(请参阅下面的“快速…”宏)。这一切都很有效。当我在gdb中反汇编代码时,我看到快速。。。宏在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, s2, s3, i1, i2, i3);

        s1 = 0x1234;
        fastputword(&s2, s1);
        s3 = fastword(&s2);
        i1 = 0x12345678;
        fastputdword(&i2, i1);
        i3 = fastdword(&i2);
        printf("s1=%x, s2=%x, s3=%x, i1=%x, i2=%x, i3=%x\n", s1, s2, s3, i1, i2, i3);
}
typedef无符号字符;
typedef无符号短ushort;
typedef无符号整数单元;
#定义单词(a)(ushort)((*((uchar*)(a))>8)和0x00ff)\
*((char*)(a)+1)=(char)((ushort)((w)>>0))和0x00ff)
#定义dword(a)(uint)((uint)(单词(a))>24)和0x00ff)\
*((字符*)(a)+1)=(字符)((uint)((d)>>16))&0x00ff\
*((字符*)(a)+2)=(字符)((uint)((d)>>8))和0x00ff)\
*((字符*)(a)+3)=(字符)((uint)((d)>>0))和0x00ff)
#定义快速字(a)(ushort)uuu内置的bswap16(*(ushort*)a));
#定义fastputword(a,w)*((ushort*)a)=内置bswap16((ushort)w);
#定义fastdword(a)(uint)内置bswap32(*(uint*)a);
#定义快速计算字(a,d)*((uint*)a)=内置bswap32((uint)d);
int main()
{
无符号短s1、s2、s3;
无符号整数i1,i2,i3;
s1=0x1234;
putword(&s2,s1);
s3=单词(&s2);
i1=0x12345678;
普特福德(i2、i1);
i3=dword(&i2);
printf(“s1=%x,s2=%x,s3=%x,i1=%x,i2=%x,i3=%x\n”,s1,s2,s3,i1,i2,i3);
s1=0x1234;
fastputword(&s2,s1);
s3=快速字(&s2);
i1=0x12345678;
fastputdword(&i2,i1);
i3=快速DWORD(&i2);
printf(“s1=%x,s2=%x,s3=%x,i1=%x,i2=%x,i3=%x\n”,s1,s2,s3,i1,i2,i3);
}

我只会使用
htons、htonl
和朋友。它们的可移植性更强,而且很可能任何给定libc的作者都将它们实现为内联函数或宏,这些函数或宏调用
\u内置的
内部函数或内联asm或其他任何东西,从而为特定的机器提供近乎最佳的实现,我认为这是Linux/glibc的一种风格


您确实需要对它们进行优化以使其内联,否则它会生成一个普通的函数调用。但是,即使是
-Og
也会将它们内联起来,并且不应该把调试搞得一团糟。无论如何,如果您在编译时没有进行任何优化,那么整个程序将效率低下,因此调用
htons
的额外指令肯定是您最不担心的。

*((char*)(a))=
如果您使用普通类型而不是单个字节,编译器将足够聪明,可以将其优化为相同的代码。。。为什么到处都使用
char
?代码是不等价的-如果
a
未与
ushort
uint
对齐,则会出现seg错误-因此,您看到的“改进”减少了指令,因为编译器可以使用对齐指令。它与
\uuuuBuiltin\uBSWAP*
关系不大,但更多的是您使用
*((ushort*)a=
而不是
*(char*)a=
赋值。我尝试修改我的测试程序,通过bswap32专门强制进行不对齐的dword访问,它可以工作。因此,我猜X86_64机器在获取短或int时不关心16位和32位内存对齐。请参阅和
将指向指针p的本机16位变量(w)存储为16位big-endian。
是否保证与16位big-endian对齐?
w
保证与16位对齐吗?是的,保证16位和32位对齐。此数据库源于IBM Series/1,它是big-endian,不支持未对齐的16位和32位数据获取/存储。事实上,数据库是一个系统的一部分,在这个系统中,少量遗留的Series/1汇编代码仍然通过软件仿真层运行。与只需交换数据库字段的新代码相比,该仿真层可能从快速字节交换中获得更多的好处…嗯。我对内置bswap函数的使用仅限于config.h文件中的宏定义-仅在已知有内置bswap的系统上使用。因此,如果使用HTON需要进行优化,那么我更愿意直接访问“源代码”。至于我是否会看到任何速度优势,我想这还有待观察——但是(请参阅我在原始帖子中的评论),对于Series/1机器仿真,我认为其好处将是显著的。