C++ / Boost ASIO 中的内联 ntohs() / ntohl()

3

大家好,我正在使用C++/Boost ASIO,并且为了提高性能,我需要内联使用ntohl()。 每个数据包包含256个int32s,因此需要大量调用ntohl()。 有人这样做过吗?

以下是在VC10++上进行所有优化编译的汇编输出:

;  int32_t d = boost::asio::detail::socket_ops::network_to_host_long(*pdw++);
mov      esi, DWORD PTR _pdw$[esp+64]
mov      eax, DWORD PTR [esi]
push     eax
call     DWORD PTR __imp__ntohl@4

我也尝试过winsock提供的常规ntohl()。 如果有帮助,将不胜感激。

此外,我一直在考虑采用C语言的#define宏来进行简单的int32位移(如果网络顺序与机器顺序在编译时不匹配)。 如果有人知道并能提供在x86 / x64架构上最有效的ntohl()汇编,那就太棒了。 最终我的代码还需要可移植到ARM。


1
你正在使用的操作系统和平台是什么? - user405725
2
顺便说一下,如果你使用Boost ASIO并认为调用“ntohl”是性能瓶颈的话,那你就错得离谱了 :) - user405725
目前支持Windows 7和x64平台,很快也可能会支持Linux x64平台。最终代码将在带有Linux的ARM Cortex-A9平台上运行。 - Mark
1
分析器告诉我,我只有一个套接字/一个线程,并且正在读取UDP数据报,问题是数据速率接近70MB/s或~70,000个数据包/秒。软件CRC检查也是一个巨大的瓶颈,但正在移入硬件中。 - Mark
你确定你有一个自定义的处理程序分配器,避免调用malloc/free/new/delete吗?如果没有,那么你的分析器是如何显示对ntohl的调用而不是malloc的呢?或者你是在分析进程所花费的CPU周期,排除阻塞系统调用吗? - user405725
3个回答

5

x86-32和x86-64平台有一个32位的'bswap'汇编指令。我认为您不会比这个操作更好。

uint32_t asm_ntohl(uint32_t a)
{
   __asm
    {
       mov eax, a;
       bswap eax; 
    }
}

谢谢,工作几乎完美无缺,除了微软编译器插入了2个指令来备份'a'到堆栈中,所以我使用了内置的_byteswap_ulong()函数。这里是相关主题的链接如果允许的话,我可能会转向英特尔编译器。 :) - Mark
MSVC 不支持在 x64/amd64 上使用内嵌汇编。自 ATOM 开始,英特尔引入了 movbe 指令,但随后桌面 CPU 也支持该指令。推荐通过 CPUID 测试该功能。movbe 可用于将数据从内存加载到寄存器,并将其视为大端字节序。 - zhaorufei

1
看看汇编代码,__imp_ntohl@4是DLL中的导入符号,因此它是外部函数,不能被内联。

当然,您可以编写自己的版本,甚至可以使用宏,知道您很可能在小端机器上使用Windows,您只需要交换字节即可。

您可以在glib的
gtypes.h
头文件中找到几个高度优化的版本,或多或少是可移植的版本,宏GUINT32_SWAP_LE_BEglib.h


给其他人的一条提示:glib.h 是为 x86 与 GNU C 编译器进行了优化,对于其他架构/编译器,它会执行 <<|,就像 #define 宏和常规 C 一样。 - Mark

1
请查看优化字节交换以获得乐趣和利润。它解释了如何使其更快。
但我强烈建议您停止担心它。想想看 - 每次调用async_read时,ASIO都会分配内存来存储处理程序的状态,仅举一个例子。这远比调用默认情况下在Linux中内联的无辜ntohl要昂贵得多。看起来你有一个过早优化的问题 - 你应该立即停止,否则你将浪费时间和资源。毕竟 - 先对应用程序进行分析,然后再进行优化(建议使用vTune或TotalView)。

我正在线程中使用sync_read来避免mallocs并传递相同的预分配缓冲区。不过你说得对,我现在还为时过早地进行了优化。我一定会去看看vTune,谢谢! - Mark
@Mark:告诉你吧 - 即使你使用相同的线程和预分配缓冲区,你仍然不能避免内存分配。调用async_read需要将附加状态与操作相关联,并且ASIO会自动为此分配/释放内存,除非你使用自定义处理程序分配器。所以你走了正确的道路 - 先进行性能分析 :-) - user405725
抱歉,在我上一篇帖子中我说了“sync_read”,但实际上我调用的是m_socket.receive_from(boost::asio::buffer(m_packet_buffer), m_remote_ep),因此没有进行内存分配?我认为它应该直接映射到类似BSD风格的套接字调用? - Mark
@Mark:糟糕,对于同步调用,可能没有分配,因为您没有必须以异步方式调用的回调函数,但您必须仔细检查。当推动到极限时,Asio是非常纯粹的表现者,所以要保持警惕 :) - user405725

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接