从stdout读取的奇怪的性能问题

时间:2012-06-01 17:50:25

标签: c++ linux ipc stdout c++-standard-library

我正在研究一些用于测试其他可执行文件的代码。为方便起见,我将把我的代码称为测试人员,并将代码作为客户端进行测试。测试人员将产生客户端并将命令发送到客户端的stdin并从客户端的stdout接收结果。

我想先做一些性能测试,所以我写了一个非常简单的示例测试器和客户端。测试人员等待客户端向其标准输出写入“READY”,作为响应,它将“GO”发送到客户端的标准输出。然后客户端将一些字节写入stdout,通过命令行标志配置,然后写入“\ nREADY \ n”,此时测试仪将再次写入“GO”。重复10,000次,之后我计算完成测试所需的时间和“吞吐量”,10,000除以完成时间。

我运行上述测试,客户端在发送“READY”之前发送0,10,100,1000,10000和100000字节的数据。对于每个字节大小,我重复测试10次并取平均值。当我在Ubuntu VMWare实例的笔记本电脑上运行时,我的吞吐量大约为每秒100k GO / READY对。性能相当稳定,几乎不依赖于客户端发送给测试仪的二进制字节数。然后我在运行CentOS的非常快速的24核服务器上重复测试。使用0字节有效负载时,我每秒仅观察到约55k GO / READY对,并且性能随着客户端发送的字节数的增加而显着降低。当客户端在“GO”和“READY”之间发送100k字节时,吞吐量仅为每秒约6k次操作。

所以我有三个问题

  1. 为什么相同的代码在更快的机器上运行得慢得多
  2. 为什么虚拟机中的性能与有效负载大小无关,但快速服务器上的性能在很大程度上取决于有效负载大小?
  3. 我能做些什么来加快服务器上的速度
  4. 一种可能的解释是我在快速服务器上重新编译了代码,它使用的是不同版本的C ++库。 VMWare计算机正在运行Ubuntu 11.10,而快速服务器正在运行CentOS 6.两者都是64位计算机。

    相关测试人员代码如下:

    ios_base::sync_with_stdio(false);
    const int BUFFER_SIZE = 2 << 20;
    char buffer[BUFFER_SIZE];
    process_stdout->rdbuf()->pubsetbuf(buffer, BUFFER_SIZE);
    Timer timer;
    // Wait until the process is ready
    string line;
    line.reserve(2 << 20);
    getline(*process_stdout, line);
    CHECK(line == "READY");
    timer.Start();
    for (int i = 0; i < num_trials; ++i) {
      *process_stdin << "GO\n";
      process_stdin->flush();
      line = "";
      while (line != "READY") {
        getline(*process_stdout, line);
      }
    }
    double elapsed = timer.Elapsed();
    cout << "Done. Did " << num_trials << " iterations in "
         << elapsed << " seconds. Throughput: "
         << double(num_trials) / elapsed << " per second." << endl;
    

    我还尝试使用read()调用(来自unistd.h)到1MB缓冲区的版本,并调用memchr找到“\ n”字符并查找READY但得到相同的性能结果。

    相关客户代码如下:

    // Create a vector of binary data. Some portion of the data will be sent 
    // to stdout each time a "GO" is received before sending "READY"
    vector<char> byte_source;
    const int MAX_BYTES = 1 << 20;
    for (int i = 0; i < MAX_BYTES; ++i) {
     byte_source.push_back(i % 256);
    }
    
    cout << "READY" << endl;
    while (cin.good()) {
      string line;
      getline(cin, line);
      if (line == "GO") {
        // The value of response_bytes comes from a command line flag
        OutputData(response_bytes, byte_source);
        cout << "READY" << endl;
      }
    }
    
    // write bytes worth of data from byte_source to stdout
    void OutputData(unsigned int bytes,
                    const vector<char>& byte_source) {
      if (bytes == 0) {
        return;
      }
      cout.write(&byte_source[0], bytes);
      cout << "\n";
    }
    

    非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

VM中的速度与有效负载大小无关的事实表明您做错了什么。这些不是完整的程序,所以很难确定什么。使用strace查看正在发生的事情,即客户端是否确实发送了您认为的所有数据(并检查测试人员是否正在接收所有数据)。

100k READY / GO对太多了;它基本上接近每秒上下文切换次数的上限,没有做任何其他事情。