0xAA55 发表于 2018-6-12 12:56:05

【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

唐凌 发表于 2018-6-12 13:13:52

我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位版本。你试试看?

0xAA55 发表于 2018-6-12 15:20:23

tangptr@126.com 发表于 2018-6-12 13:13
我记得微软的编译器有_byteswap_ulong这个内置宏,还有_byteswap_uint64,_byteswap_ushort的对应64位,16位 ...

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

Ayala 发表于 2018-6-12 19:13:32

bswap指令386的处理器不支持。

0xAA55 发表于 2018-6-13 16:15:43

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:25

这多艹蛋蛋每次都转感觉怪怪的   传过来的图都是反色的蛋蛋疼

0xAA55 发表于 2018-6-13 16:42:19

sml2 发表于 2018-6-13 16:40
这多艹蛋蛋每次都转感觉怪怪的   传过来的图都是反色的蛋蛋疼

和图有什么卵关系

sml2 发表于 2018-6-13 16:46:02

0xAA55 发表于 2018-6-13 16:42
和图有什么卵关系

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

0xAA55 发表于 2018-6-13 18:31:18

sml2 发表于 2018-6-13 16:46
自己写的Linux截图进程 截完后再传到Win上图全是反色的 RBG 变成了 GBR

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

sml2 发表于 2018-6-14 11:25:42

0xAA55 发表于 2018-6-13 18:31
RGB和BGR与字节顺序无关,是图像颜色格式的概念了。

我懂你意思 问题是Integer传过去也反了...

3239066163 发表于 2018-8-6 09:12:28

谢谢分享
页: [1]
查看完整版本: 【C】且看自己实现的字节顺序转换语句无法被MSVC和GCC优化