Python和C ++程序与子进程之间的通信非常慢

时间:2016-12-23 23:03:06

标签: python c++ subprocess pipe

我试图通过使用子进程Python模块将一些重复计算外包到C ++程序来加速Python程序。

为了说明我的问题,我采用了一个简单的C ++代码,它返回了输入的两倍。一百万个整数需要16秒,这似乎很慢。

这是C ++程序(double.exe):

#include <iostream>

using namespace std;

int main()
{
    int a;
    bool goon = true;
    while (goon)
    {
        cin >> a;
        cout << 2 * a << endl;
        if (a == 0)
            goon= false;
    }
}

这里是Python 3代码:

from time import time
from subprocess import PIPE,Popen

cmd = ["double"]
process = Popen(cmd, stdin=PIPE,stdout=PIPE, bufsize=32,universal_newlines=True, shell=True)

t0 = time()
for i in range(1,int(1e6)):
    print(i, file=process.stdin, flush=True)
    output = int(process.stdout.readline())
dt = time() - t0
print("Time to communicate : %fs" % dt)
print(0,file=process.stdin,flush=True) # close 'double' program
  

沟通时间:16.029137s

对我而言,它如此之慢的原因只能是通过管道进行Python进程和C ++程序之间的通信,但我还没有找到如何加速它。使用子进程或其他库加速此通信的任何解决方案?

我在Windows上使用Python 3.5.2。

2 个答案:

答案 0 :(得分:3)

问题不是stdin通信本身,而是大规模的上下文切换。你做了一个非常小的任务&#34;在C ++代码中,但对于每个这样的&#34;任务&#34; python代码应该将数据写入管道,刷新,睡眠,C ++部分唤醒,解析输入,计算结果,打印输出,刷新并进入睡眠状态。然后python代码唤醒,等等。

进入睡眠和醒来(以及相关的上下文切换)并不是免费的。随着&#34;任务的大小&#34; (将输入乘以2)这种开销大部分时间消耗。

你可以修复&#34;通过批量提供工作到C ++程序,或者有更大的任务。或两者兼而有之。

例如,如果在每次写入后刷新管道,那么使用数百万个数字的相同作业,但使用10个数字的批次在我的盒子上运行速度快2倍。代码:

for i in range(1,int(1e5)):
    for j in range(1, 10):
        print(i*10 + j, file=process.stdin, flush=True)
    for j in range(1, 10):
        output = int(process.stdout.readline())

如果每10个号码只进行一次刷新,它的运行速度比前一个例子慢1.5倍(或者比原始代码快3倍):

for i in range(1,int(1e5)):
    for j in range(1, 10):
        print(i*10 + j, file=process.stdin)
    process.stdin.flush()
    for j in range(1, 10):
        output = int(process.stdout.readline())

如果&#34;任务&#34;然后,您必须为上下文切换支付的价格相同。但与任务规模相比,它并没有那么大。例如,让我们想象上下文切换需要0.1秒(在现实生活中它的方式更小,这只是一个例子)。如果任务是乘法,例如1ms(再次,仅举例),那么与任务相比,上下文切换开销是10000%。但如果您的任务很繁重并且需要执行1秒,则开销仅为10%。相对值的1000倍差异。

答案 1 :(得分:1)

只是一个猜测,但可能是因为std::endl不仅写了一个新的行字符而且还刷新了输出流。冲洗之夜是花费最多时间的部分。因此,如果你只是写

,它可能会更快
std::cout << 2 * a << "\r\n"; //Windows style line break

{{1}}

(注意:未经测试是否有效或实际上需要隐式刷新。)