【C】且看自己实现的字节顺序转换语句无法被MSVC和GCC优化
#include<stdio.h>#include<stdlib.h>
#include<stdint.h>
#include<string.h>
int main()
{
uint32_t a;
scanf("%x", &a);
printf("%x\n", ((a & 0x0ff) << 24) | ((a & 0x0ff00) << 8) | ((a & 0xff0000) >> 8) | ((a & 0xff000000) >> 24));
return 0;
}这份代码,用scanf从stdin读取一个十六进制数值,存入变量a,然后用printf打印,把a的字节顺序大小端转换了一下。
我预想VS大概会使用类似bswapd之类的指令来实现的吧,然而,否。
编译设置是Win32 Release,优化参数是/O2。试过/Ox效果一样。
然后看看gcc的效果。
版本:gcc version 4.4.7 20120313 (Red Hat 4.4.7-18) (GCC)
编译命令:gcc -m32 -O3 a.c -o a.o
我预想GCC大概就会使用类似bswapd之类的指令来实现的吧,然而,否。
继续测试。此时我要使用htonl、ntohl等类似函数,看会怎样。
#include<stdio.h>
#include<stdlib.h>
#include<stdint.h>
#include<string.h>
#include<winsock2.h>
int main()
{
uint32_t a;
scanf("%x", &a);
printf("%x\n", htonl(a));
return 0;
}
划重点:
00841012push dword ptr
00841015call dword ptr ds:
call 是什么鬼,跟过去看看:
一个函数指针来着。地址是0x75692d57。这应该是htonl这个API的地址了。
那么htonl是怎么实现的呢?
看!
API竟然也是用一系列啰嗦的位移等各种运算实现的字节顺序改变,而且和我自己写的那个非常像。
难道说,这种方法比用单一的一条bswapd指令更好?我们来看看GCC是怎么做的。
还是那份代码(<winsock2.h>换成<sys/socket.h>),还是那个编译命令(gcc -m32 -O3 a.c -o a.o),让我们看看效果。
gcc也是调用函数的方式调用的htonl,这是可以理解的。因为htonl并不是单纯地进行字节顺序的改变。它会根据当前系统是BE还是LE来判断要不要改变字节顺序。所以这是个API,是一个系统相关的玩意儿。不能误解它。
此处有资料:
https://stackoverflow.com/questions/21527957/htonl-vs-builtin-bswap32
根据glibc的源码,_htonl在需要进行字节顺序转换的时候,是用__builtin_bswap32实现的。所以我们要看__builtin_bswap32这个GCC钦定函数是怎么实现的。#include<stdio.h>
#include<stdlib.h>
#include<inttypes.h>
#include<string.h>
#include<sys/socket.h>
int main()
{
uint32_t a;
scanf("%x", &a);
printf("%x\n", __builtin_bswap32(a));
return 0;
}
GCC的__builtin_bswap32在x86平台上确实就是用bswap(严格来说32位版本的bswap写作bswapd)指令实现的,而且是内联的。
补充:
根据4楼的补充,386没有bswap,而Win7的x86版本是兼容386的。所以要自己运算实现。
但我仍然觉得这是Win7优化不足,因为如果是我来实现的话,指令会更简短,并且对内存的读写会减少。详见5楼。
结论:
1、MSVC和GCC都不能把以下的宏优化为你所想的单条指令实现字节顺序变换。
#define byteswapd(a) ((((a) & 0x0ff) << 24) | (((a) & 0x0ff00) << 8) | (((a) & 0xff0000) >> 8) | (((a) & 0xff000000) >> 24))
2、Windows平台上的htonl作为API,竟然也是这么蠢,没有使用bswapd,更像是上述宏的展开。(Windows 7 旗舰版 x64+VS2012)
3、GCC有__builtin_bswap32这个内置钦定函数可以用于32位的字节顺序转换,用了bswapd,并且x86平台Linux上的htonl这个API也是用的这个指令。
参考资料:
htonl() vs __builtin_bswap32()
https://stackoverflow.com/questions/21527957/htonl-vs-builtin-bswap32
Other Built-in Functions Provided by GCC
https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html 我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位版本。你试试看? tangptr@126.com 发表于 2018-6-12 13:13
我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位 ...
但!htonl这API竟然没有使用这个玩意儿来优化。 bswap指令386的处理器不支持。 Ayala 发表于 2018-6-12 19:13
bswap指令386的处理器不支持。
查看了一下……尴尬,还真是这样。
但如果是我,我会这样用汇编来实现:
mov eax,要转换的数(比如)
mov dx,ax
shr eax,16
xchg dl,dh
xchg al,ah
shl edx,16
or eax,edx
ret
这多艹蛋蛋每次都转感觉怪怪的 传过来的图都是反色的蛋蛋疼 sml2 发表于 2018-6-13 16:40
这多艹蛋蛋每次都转感觉怪怪的 传过来的图都是反色的蛋蛋疼
和图有什么卵关系 0xAA55 发表于 2018-6-13 16:42
和图有什么卵关系
自己写的Linux截图进程 截完后再传到Win上图全是反色的 RBG 变成了 GBR sml2 发表于 2018-6-13 16:46
自己写的Linux截图进程 截完后再传到Win上图全是反色的 RBG 变成了 GBR
RGB和BGR与字节顺序无关,是图像颜色格式的概念了。 0xAA55 发表于 2018-6-13 18:31
RGB和BGR与字节顺序无关,是图像颜色格式的概念了。
我懂你意思 问题是Integer传过去也反了... 谢谢分享
页:
[1]