多线程和stdout的性能问题

时间:2012-08-29 16:52:08

标签: c++ multithreading pthreads ipc

我正在开发一个基准测试应用程序,它使用用户定义的线程数来进行处理。我也正在研究基准测试结果的可视化工具。

基准测试本身是用C ++编写的(并使用pthreads进行线程处理),而可视化工具则是用Python编写的。

现在,我正在做的两件事就是从基准测试到可视化工具stdout。这样做的好处是能够使用像netcat这样的工具在一台机器上运行基准测试,在另一台机器上运行可视化器。

High level diagram of the application

关于基准测试:

  • 非常受CPU限制
  • 每个线程每隔几十毫秒写入重要数据(即可视化器所需的数据)。
  • 每个打印的数据都是5到20个字符的行。
  • 如前所述,线程数量变化很大(可以是1,2,40等)
  • 尽管重要的是数据没有被破坏(例如,一个线程在printf / cout期间抢占另一个线程,导致打印数据与另一个线程上的输出交错) ,以正确的顺序完成写入并不是很重要。

关于最后一点的例子:

// Thread 1 prints "I'm one\n" at the 3 seconds mark
// thread 2 prints "I'm two\n" at the 4 seconds mark

// This is fine
I'm two
I'm one

// This is not
I'm I'm one
 two

在基准测试中,我从std::cout切换到printf,因为它更接近write(2),以便最小化不同线程的输出之间交错的机会。

我担心随着线程数量的增加,从多个线程写入stdout将导致瓶颈。非常重要的是,基准测试的可视化输出部分对资源非常轻,以免扭曲结果。

我正在寻找有效的方法让我的两个应用程序在不影响我的基准测试的性能而不是绝对必要的情况下进行讨论。有任何想法吗?你们中的任何一个人之前都是这样解决过的吗任何更智能/更清洁的解决方案?

4 个答案:

答案 0 :(得分:3)

写入stdout不太可能成为任何现实世界问题的性能瓶颈。如果是,那么您要么记录太多,要么对任务进行基准测试,以便对背景噪声无法测量。然而,这是一个线索安全漏洞。你选择printf和cout只是伏都教 - 它们都不是线程安全的。如果要在多线程环境中使用缓冲I / O,则需要自己序列化调用(使用pthread_mutex_t,或者使用信号量实现队列等...)。如果你想依靠系统调用原子性来为你做这件事(在内部,内核完成同样的序列化),你需要自己进行系统调用而不是依赖printf"接近" ;写。

答案 1 :(得分:0)

他们都可以将输出行作为字符串推送到queue,而另一个线程可以将它们拉出来并记录它们(单线程,缓冲输出,不经常刷新)。

答案 2 :(得分:0)

首先,在我担心之前,我确定这是一个问题。如果 写入只是每10或20毫秒一次,它们可能就是这样 不会打扰任何事情。

否则:"写"实际上由两个操作组成:格式化 输出,并物理输出格式化的字节。第二 可能相当快,因为​​这只是一个复制5到20的问题 从您的进程到操作系统的字符。 (操作系统将执行物理操作 从write / WriteFile函数返回后写一次。)如果 您使用std::ostrstream在本地格式化(已弃用,但应该是 可用)或snprintf,格式化为本地char[],然后调用 关于结果的writeWriteFile,您不需要任何外部信息 同步。

或者,你可以在一个单独的线程中完成所有的写作 将请求(带有必要的数据)推入队列(这很容易 使用条件变量实现。)

答案 3 :(得分:0)

假设你有一个POSIX兼容的stdlib,每次调用stdio函数都是相对于其他线程的原子,所以只要你用一个printf调用打印出你的行,它们就不会混合即使两个线程在同一时间写一条线也在一起。对于使用C ++的每个iostream::operator<<调用,情况也是如此,但如果你写的是cout << "xxx " << var << endl;,那就是三个调用,而不是一个。

如果要对stdio函数使用多次调用并将其作为单个单元编写,则可以使用flockfile(3)。例如:

flockfile(stdout);
printf("data: ");
print_struct(foo);  // a function that calls printf internally
printf("\n");
funlockfile(stdout);

这会导致从data到新行的整个事情被打印而不允许其他线程交错。它对C ++ iostreams也很有用:

flockfile(stdout);
cout << "data: " << x << endl;
funlockfile(stdout);