找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 7827|回复: 10

【C】且看自己实现的字节顺序转换语句无法被MSVC和GCC优化

[复制链接]

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24235 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
发表于 2018-6-12 12:56:05 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<stdint.h>
  4. #include<string.h>

  5. int main()
  6. {
  7.         uint32_t a;
  8.         scanf("%x", &a);

  9.         printf("%x\n", ((a & 0x0ff) << 24) | ((a & 0x0ff00) << 8) | ((a & 0xff0000) >> 8) | ((a & 0xff000000) >> 24));
  10.         return 0;
  11. }
复制代码
这份代码,用scanf从stdin读取一个十六进制数值,存入变量a,然后用printf打印,把a的字节顺序大小端转换了一下。

我预想VS大概会使用类似bswapd之类的指令来实现的吧,然而,否。

bswapd.png

编译设置是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.png

我预想GCC大概就会使用类似bswapd之类的指令来实现的吧,然而,否。

继续测试。此时我要使用htonl、ntohl等类似函数,看会怎样。

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<stdint.h>
  4. #include<string.h>
  5. #include<winsock2.h>

  6. int main()
  7. {
  8.         uint32_t a;
  9.         scanf("%x", &a);

  10.         printf("%x\n", htonl(a));
  11.         return 0;
  12. }
复制代码
htonl.png

asm.png

划重点:
00841012  push        dword ptr [esp]  
00841015  call        dword ptr ds:[84B108h]  

call [84B108h]是什么鬼,跟过去看看:
mem.png
一个函数指针来着。地址是0x75692d57。这应该是htonl这个API的地址了。
那么htonl是怎么实现的呢?
看!
api.png
API竟然也是用一系列啰嗦的位移等各种运算实现的字节顺序改变,而且和我自己写的那个非常像。
难道说,这种方法比用单一的一条bswapd指令更好?我们来看看GCC是怎么做的。

还是那份代码(<winsock2.h>换成<sys/socket.h>),还是那个编译命令(gcc -m32 -O3 a.c -o a.o),让我们看看效果。
gcc_.png
gcc也是调用函数的方式调用的htonl,这是可以理解的。因为htonl并不是单纯地进行字节顺序的改变。它会根据当前系统是BE还是LE来判断要不要改变字节顺序。所以这是个API,是一个系统相关的玩意儿。不能误解它。

此处有资料:
https://stackoverflow.com/questions/
21527957
/htonl-vs-builtin-bswap32


根据glibc的源码,_htonl在需要进行字节顺序转换的时候,是用__builtin_bswap32实现的。所以我们要看__builtin_bswap32这个GCC钦定函数是怎么实现的。
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<inttypes.h>
  4. #include<string.h>
  5. #include<sys/socket.h>

  6. int main()
  7. {
  8.         uint32_t a;
  9.         scanf("%x", &a);

  10.         printf("%x\n", __builtin_bswap32(a));
  11.         return 0;
  12. }
复制代码
builtin.png
GCC的__builtin_bswap32在x86平台上确实就是用bswap(严格来说32位版本的bswap写作bswapd)指令实现的,而且是内联的。

补充:
根据4楼的补充,386没有bswap,而Win7的
x
86版本是兼容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/
on
linedocs/gcc/Other-Builtins.html

本帖被以下淘专辑推荐:

回复

使用道具 举报

65

主题

115

回帖

1万

积分

用户组: 超级版主

OS与VM研究学者

UID
1043
精华
35
威望
789 点
宅币
8292 个
贡献
1094 次
宅之契约
0 份
在线时间
2067 小时
注册时间
2015-8-15
发表于 2018-6-12 13:13:52 | 显示全部楼层
我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位版本。你试试看?
回复 赞! 靠!

使用道具 举报

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24235 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2018-6-12 15:20:23 | 显示全部楼层
tangptr@126.com 发表于 2018-6-12 13:13
我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位 ...

但!htonl这API竟然没有使用这个玩意儿来优化。
回复 赞! 靠!

使用道具 举报

30

主题

210

回帖

2778

积分

用户组: 版主

UID
1821
精华
7
威望
69 点
宅币
2159 个
贡献
206 次
宅之契约
0 份
在线时间
480 小时
注册时间
2016-7-12
发表于 2018-6-12 19:13:32 | 显示全部楼层
bswap指令386的处理器不支持。
回复 赞! 靠!

使用道具 举报

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24235 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2018-6-13 16:15:43 | 显示全部楼层
Ayala 发表于 2018-6-12 19:13
bswap指令386的处理器不支持。


查看了一下……尴尬,还真是这样。
但如果是我,我会这样用汇编来实现:
mov eax,要转换的数(比如[esp+4])
mov dx,ax
shr eax,16
xchg dl,dh
xchg al,ah
shl edx,16
or eax,edx
ret
回复 赞! 靠!

使用道具 举报

1

主题

24

回帖

138

积分

用户组: 小·技术宅

UID
2928
精华
0
威望
1 点
宅币
111 个
贡献
0 次
宅之契约
0 份
在线时间
15 小时
注册时间
2017-10-7
发表于 2018-6-13 16:40:25 | 显示全部楼层
这多艹蛋蛋  每次都转  感觉怪怪的   传过来的图都是反色的  蛋蛋疼
回复 赞! 靠!

使用道具 举报

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24235 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2018-6-13 16:42:19 | 显示全部楼层
sml2 发表于 2018-6-13 16:40
这多艹蛋蛋  每次都转  感觉怪怪的   传过来的图都是反色的  蛋蛋疼

和图有什么卵关系
回复 赞! 靠!

使用道具 举报

1

主题

24

回帖

138

积分

用户组: 小·技术宅

UID
2928
精华
0
威望
1 点
宅币
111 个
贡献
0 次
宅之契约
0 份
在线时间
15 小时
注册时间
2017-10-7
发表于 2018-6-13 16:46:02 | 显示全部楼层
0xAA55 发表于 2018-6-13 16:42
和图有什么卵关系

自己写的Linux截图进程 截完后再传到Win上  图全是反色的 RBG 变成了 GBR
回复 赞! 靠!

使用道具 举报

1111

主题

1651

回帖

7万

积分

用户组: 管理员

一只技术宅

UID
1
精华
244
威望
743 点
宅币
24235 个
贡献
46222 次
宅之契约
0 份
在线时间
2296 小时
注册时间
2014-1-26
 楼主| 发表于 2018-6-13 18:31:18 | 显示全部楼层
sml2 发表于 2018-6-13 16:46
自己写的Linux截图进程 截完后再传到Win上  图全是反色的 RBG 变成了 GBR

RGB和BGR与字节顺序无关,是图像颜色格式的概念了。
回复 赞! 靠!

使用道具 举报

1

主题

24

回帖

138

积分

用户组: 小·技术宅

UID
2928
精华
0
威望
1 点
宅币
111 个
贡献
0 次
宅之契约
0 份
在线时间
15 小时
注册时间
2017-10-7
发表于 2018-6-14 11:25:42 | 显示全部楼层
0xAA55 发表于 2018-6-13 18:31
RGB和BGR与字节顺序无关,是图像颜色格式的概念了。

我懂你意思 问题是Integer传过去也反了...
回复 赞! 靠!

使用道具 举报

4

主题

16

回帖

50

积分

用户组: 小·技术宅

UID
4127
精华
0
威望
0 点
宅币
30 个
贡献
0 次
宅之契约
0 份
在线时间
1 小时
注册时间
2018-8-6
发表于 2018-8-6 09:12:28 | 显示全部楼层
谢谢分享
回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-19 23:20 , Processed in 0.048405 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表