为什么libcurl多接口在我的一些系统上慢得离谱?

2 人关注

我正在尝试用C++编写一个使用libcurl多接口的小程序,遇到了一个问题,它的执行速度比预期的要慢得多:对于一个静态HTML页面(由我在互联网上租用的小型虚拟机上的nginx提供)的49个请求(任意数量),它需要相当稳定的32秒。

在谷歌或GitHub上运行应用程序也需要同样长的时间,而在我的静态页面上使用 hey yields >1500 requests/s, so I'm pretty sure it's not the servers fault. Looping over the curl command line application 50 times in a row is also quite a bit faster with just 13 seconds total.

下面是有关的代码(为了便于阅读,代码最小)。

#include <curl/curl.h>
#include <memory>
#include <string>
#include <vector>
struct Request {
  std::shared_ptr<CURL> request;
int request(std::string url, CURLM *curlm, std::vector<Request> &requests) {
  Request r = {
    .request = std::shared_ptr<CURL>(curl_easy_init(), curl_easy_cleanup),
  curl_easy_setopt(r.request.get(), CURLOPT_URL, url.c_str());
  curl_multi_add_handle(curlm, r.request.get());
  requests.push_back(r);
  return 0;
int main(__attribute__((unused)) int argc, char *argv[]) {
  curl_global_init(CURL_GLOBAL_ALL);
  std::vector<Request> requests;
  CURLM *multi = curl_multi_init();
  // generate an arbitrary amount of requests
  for(int i = 0; i < 49; i++)
    request(std::string(argv[1]), multi, requests);
  int running = 0;
  curl_multi_perform(multi, &running);
    int numfds = 0;
    curl_multi_perform(multi, &running);
    // select
    fd_set fdread, fdwrite, fdexcep;
    FD_ZERO(&fdread);
    FD_ZERO(&fdwrite);
    FD_ZERO(&fdexcep);
    struct timeval timeout = {
      .tv_sec = 0,
      .tv_usec = 500
    curl_multi_fdset(multi, &fdread, &fdwrite, &fdexcep, &numfds);
    select(numfds+1, &fdread, &fdwrite, &fdexcep, &timeout);
    // or poll
    //curl_multi_poll(multi, nullptr, 0, 1000, &numfds);
  } while(running > 0);
  return 0;

我在多个客户端系统上进行了测试:我的工作站是Ubuntu 20.04,结果可以在CentOS 8、Ubuntu 18.04和Debian 10(都是libcurl 7.60系列)上重现,然而在CentOS 7(libcurl 7.29.0)上,同样的代码在3-5秒内完成!除了我的工作站,我测试的所有这些系统都是新安装的虚拟机,运行在同一台主机上,有相同的网络配置。除了我的工作站之外,我测试的这些系统都是新安装的虚拟机,运行在同一台主机上,具有相同的网络配置。同时,互联网上的虚拟机(Debian 10,libcurl 7.64)和我期望的一样快,结果是亚秒。

使用Wireshark,我可以看到所有与网络服务器的连接都是一次性打开的,但实际的HTTP请求是分批发送的,而且间隔时间越来越长--最后一个请求是在打开连接后~30秒才发送的。

select()与使用curl_multi_poll()的方式之间也没有时间上的差异,我已经翻阅了cURL文档,寻找可能提高性能的选项;启用流水线并没有带来任何区别,禁用HTTP2也没有,而且大多数限制(例如最大连接数)似乎都被默认设置为无限。

现在有趣的部分来了:当我通过strace运行这个应用程序时,在任何本地虚拟机上每次运行只需要2-4秒。起初我认为strace可能会破坏事情,但是如果我通过shell重定向丢弃strace的输出,看起来应用程序是正常工作的,那么是什么原因?

为什么使用strace可以改善事情,什么原因可能导致运行时间在这些系统中出现巨大的差异?

有什么想法,我错过了什么?我的Google-fu这次离开了我。

3 个评论
ti7
也许是你的网络对IPv6的支持不恰当?
@ti7 在本地机器上禁用了IPv6支持(因为对它的支持确实很糟糕),但我认为它不可能在这里出错,因为连接都是在开始时打开的,只是请求本身被推迟了。
这可能是操作系统或网络服务器的配置问题。批量处理和strace的效果表明,这是某种缓存。
c++
linux
libcurl
user735624545
user735624545
发布于 2021-06-20
1 个回答
273K
273K
发布于 2021-06-20
已采纳
0 人赞同

curl_multi_fdset 只添加它自己的描述符,它不会清零或以其他方式删除任何其他描述符。你必须在 curl_multi_fdset 之前做以下工作