c++ - 从 stdout 读取奇怪的性能问题

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

我正在编写一些将用于测试其他可执行文件的代码。为方便起见,我将我的代码称为测试器,将被测试的代码称为客户端。测试器将生成客户端并将命令发送到客户端的标准输入并从客户端的标准输出接收结果。

我想先做一些性能测试,所以我写了一个非常简单的示例测试器和客户端。测试器等待客户端将“READY”写入其标准输出,并作为响应将“GO”发送到客户端的标准输入。然后客户端将一定数量的字节写入标准输出,通过命令行标志配置,然后写入“\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. 我能做些什么来加快服务器上的速度

一个可能的解释是我在快速服务器上重新编译了代码,它使用了不同版本的 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";
}

如有任何帮助,我们将不胜感激!

最佳答案

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

10 万对 READY/GO 太多了;在不做任何其他操作的情况下,它基本上接近每秒上下文切换次数的上限。

关于c++ - 从 stdout 读取奇怪的性能问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10855038/

相关文章:

c++ - 更好的事件处理机制?

Linux 内核设备驱动程序从设备到用户空间内存的 DMA

c++ - 线程间通信

c# - 主从通信的IPC机制

linux - 如何清理以空格分隔的文本中未对齐的列?

c++ - Linux IPC - 多个编写器,单个读取器

c++ - 在类中使用静态成员对象有什么问题?

c++ - 使用 Jenkins 管理程序版本、内部版本号和 svn 修订版

c++ - 二进制搜索中 mid=(beg+end)/2 和 mid=beg+(end-beg)/2 有什么区别?

c - 如何从内核中的 struct file* 中获取 "file name"?