【问题标题】:Inline ntohs() / ntohl() in C++ / Boost ASIOC++ 中的内联 ntohs() / ntohl() / Boost ASIO
【发布时间】:2011-11-22 06:04:47
【问题描述】:

您好,我正在使用 C++ / Boost ASIO,出于性能原因,我必须内联 ntohl()。每个数据包包含 256 个 int32,因此对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()。任何帮助将不胜感激。

另外,我一直在考虑使用 #define 宏的 C 方式来执行简单的 int32 桶形移位(如果网络顺序与编译时的机器顺序不匹配)。如果有人知道并且可以在 x86 / x64 架构上为ntohl() 提供最有效的组装,那就太棒了。最终我的代码也需要移植到 ARM。

【问题讨论】:

  • ...而您使用的操作系统和平台是....?
  • 顺便说一句,如果您使用 Boost ASIO 并认为调用 ntohl 是性能瓶颈,那您就大错特错了 :)
  • 目前是 Windows 7 和 x64 平台,可能很快就会有 linux x64 平台。最终代码将在带有 linux 的 ARM Cortex-A9 平台上。
  • 分析器告诉我它是。我只有一个套接字/一个线程及其读取 UDP 数据报,问题是数据速率接近 70MB/s 或 ~70,000 个数据包/s。软件 CRC 检查也是一个巨大的瓶颈,但它正在被转移到硬件中。
  • 您确定您有一个自定义处理程序分配器来避免调用 malloc/free/new/delete 吗?如果不是,您的分析器如何显示您对ntohl 的调用而不是对malloc 的调用?或者您可能会分析进程占用的 CPU 周期(不包括阻塞系统调用)?

标签: c++ inline boost-asio endianness


【解决方案1】:

请参阅optimizing byte swapping for fun and profit。它解释了如何使它快速。

但我强烈建议您不要担心它。想一想——例如,每次调用async_read 时,ASIO 都会分配内存来存储处理程序的状态。顺便说一句,这比调用 Linux 中默认内联的无辜 ntohl 要贵得多。看起来你有一个过早的优化问题 - 你应该立即停止它,否则你会浪费你的时间和资源。毕竟 - 首先分析您的应用程序,然后对其进行优化(推荐使用 vTune 或 TotalView)。

【讨论】:

  • 我在线程内使用sync_read 来避免malloc 并传递相同的预分配缓冲区。不过你是对的,我过早地优化了。我也会检查 vTune,谢谢!
  • @Mark:告诉你什么——你并没有避免内存分配,即使你使用相同的线程和预分配的缓冲区。对 async_read 的调用需要与操作关联的附加状态,并且 ASIO 会为此隐式分配/释放内存,除非您有自定义处理程序分配器。所以你走的是正确的路 - 首先是个人资料:-)
  • 糟糕,我在上一篇文章中说sync_read,但实际上我正在拨打的电话是m_socket.receive_from( boost::asio::buffer(m_packet_buffer), m_remote_ep),因此没有内存分配?我认为它应该直接映射到 BSD 样式的套接字调用?
  • @Mark:糟糕,对于同步调用,可能没有 alloc,因为您没有必须以异步方式调用的回调,但您必须仔细检查。紧要关头,Asio 是非常纯粹的表演者,所以请保持警惕 :)
【解决方案2】:

看汇编器,__imp__ntohl@4是一个DLL的导入符号,所以它是一个外部函数,不能内联。

当然你可以编写自己的,甚至是宏,你知道你很可能在一个 little-endian 机器上使用 Windows,你只需要交换字节。

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

【讨论】:

  • 给其他人的说明:glib.h 针对 x86 和 GNU C 编译器进行了优化,对于其他架构/编译器,它执行 <<| ala @987654327 @marcos 和常规 C.
【解决方案3】:

x86-32 和 x86-64 平台具有 32 位“bswap”汇编指令。我认为你不会比一次手术做得更好。

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

【讨论】:

  • 谢谢,工作几乎完美无缺,除了 Microsoft 编译器插入了 2 条指令将 a 备份到堆栈上,所以我使用了内在的 _byteswap_ulong() Link to topic on this. 如果我可以切换到 Intel 编译器我被允许。 :)
  • MSVC 不支持 x64/amd64 上的内联汇编。 Intel 从 ATOM 开始引入了 movbe 指令,但后来桌面 CPU 也支持该指令。建议通过 CPUID 测试此功能。 movbe 可用于将数据从内存加载到寄存器,将其视为大端。
猜你喜欢
  • 2011-01-26
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-05
相关资源
最近更新 更多