【问题标题】:Unexpected Socket CPU Utilization意外的套接字 CPU 利用率
【发布时间】:2011-07-13 20:40:13
【问题描述】:

我遇到了一个我不明白的性能问题。我正在处理的系统有两个线程,看起来像这样:

版本 A:

  • 线程 1:数据处理 -> 数据选择 -> 数据格式化 -> FIFO
  • 线程 2:FIFO -> 套接字

“选择”对数据进行精简,线程 1 末尾的 FIFO 是线程 2 开头的 FIFO(FIFO 实际上是 TBB 并发队列)。出于性能原因,我将线程更改为如下所示:

B 版:

  • 线程 1:数据处理 -> 数据选择 -> FIFO
  • 线程 2:FIFO -> 数据格式化 -> 套接字

最初,这种优化被证明是成功的。线程 1 具有更高的吞吐量。我对 Thread 2 的性能并没有太在意,因为我预计 CPU 使用率会更高,而且(由于数据细化)这不是主要问题。但是,我的一位同事要求对版本 A 和版本 B 进行性能比较。为了测试设置,我让线程 2 的套接字(boost asio tcp 套接字)写入同一个盒子(127.0.0.1)上的 iperf 实例显示最大吞吐量的目标。

为了比较这两种设置,我首先尝试强制系统以 500 Mbps 的速度从套接字写入数据。作为性能测试的一部分,我监控了顶部。我所看到的让我感到惊讶。版本 A 没有出现在“top -H”上,iperf 也没有出现(这实际上是怀疑的)。但是,B 版(我的“增强版”)显示在“top -H”上,CPU 利用率约为 10%,而(奇怪的是)iperf 显示为 8%。

显然,这暗示我做错了什么。不过,我似乎无法证明我是!我已经确认的事情:

  • 两个版本都向套接字提供 32k 块数据
  • 两个版本都使用相同的 boost 库 (1.45)
  • 两者具有相同的优化设置 (-O3)
  • 两者都接收完全相同的数据,写出相同的数据,并以相同的速率写入。
  • 两者都使用相同的阻塞写入调用。
  • 我正在使用完全相同的设置 (Red Hat) 从同一个盒子进行测试
  • 线程 2 的“格式化”部分不是问题(我将其删除并重现了问题)
  • 网络上的小数据包不是问题(我使用的是 TCP_CORK,并且我已通过 wireshark 确认 TCP 段都是 ~16k)。
  • 在套接字写入后立即休眠 1 毫秒会使套接字线程和 iperf(?!) 上的 CPU 使用率回到 0%。
  • Poor man 的分析器显示很少(套接字线程几乎总是处于休眠状态)。
  • Callgrind 显示的很少(套接字几乎不写寄存器)
  • 为 netcat 切换 iperf(写入 /dev/null)不会改变任何东西(实际上 netcat 的 cpu 使用率约为 20%)。

我唯一能想到的是,我在套接字写入周围引入了一个更紧密的循环。但是,在 500 Mbps 时,我不希望我的进程 iperf 上的 cpu 使用率会增加?

我不知道为什么会发生这种情况。我和我的同事基本上没有想法。有什么想法或建议吗?在这一点上,我很乐意尝试任何事情。

【问题讨论】:

  • 只是一个疯狂的猜测,但也许线程 2 的数据集不再适合 CPU 的缓存,而之前它可以?
  • 听起来你需要像oprofile 这样的东西来分析。

标签: c++ multithreading performance sockets boost


【解决方案1】:

如果没有代码 sn-ps 或实际数据量,这将很难分析。

想到的一件事:如果预格式化的数据流比后格式化的数据流大得多,您可能会花费更多的带宽/周期来通过 FIFO(套接字)边界复制更多数据。

尝试估计或测量每个阶段的数据速率。如果“选择”的输出数据速率更高,请考虑将格式移动到边界另一侧的影响。是不是可能配置A中select->format转换不需要copy,而配置B强加了很多copy?

...只是猜测,对系统没有更深入的了解。

【讨论】:

  • post 格式化的数据不会比 pre 格式化大多少。即使这样,也可能大 1%。进入 FIFO 的数据速率约为 500 Mbps,流出套接字的数据约为 500 Mbps,因此您可以看到数据仅发生了很小的变化。抱歉,这里不涉及更多代码。有什么特别有用的吗?我试图在这里保持简单,因为我相信我已经隔离了问题(突发写入套接字)并且代码库非常大。编辑:两种配置都需要一个副本。
【解决方案2】:

如果 FIFO 是版本 A 中的瓶颈怎么办。那么两个线程大部分时间都会坐下来等待 FIFO。在版本 B 中,您可以更快地将数据交给iperf

【讨论】:

    【解决方案3】:

    您在 FIFO 队列中究竟存储了什么?您是否存储数据包,即缓冲区?

    在版本 A 中,您将格式化数据(可能是字节)写入队列。因此,在套接字上发送它只需要写出一个固定大小的缓冲区。

    但是在版本 B 中,您将高级数据存储在队列中。格式化它现在会创建更大的缓冲区大小,这些缓冲区大小会直接写入套接字。这会导致 TCp/ip 堆栈将 CPU 周期用于分片和开销...

    这是我基于你目前所说的理论。

    【讨论】:

    • 在版本 A 和 B 中,我们将缓冲区存储在 FIFO 中。两个版本都一次向套接字写入 32k 块数据。而且由于 TCP_CORK 正在使用中,我已尽我所能将 TCP/IP 开销降至最低。可能是我不了解有关套接字写入的基本知识?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 2013-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多