多个子流程需要很长时间才能完成

时间:2018-12-18 18:23:38

标签: python ubuntu subprocess tesseract popen

我有一个使用subprocess模块的Popen运行的进程:

result = subprocess.Popen(['tesseract','mypic.png','myop'])
st = time()
while result.poll() is None:
    sleep(0.001)
en = time()

print('Took :'+str(en-st))

这将导致:

Took :0.44703030586242676

在这里,进行tesseract调用以处理图像mypic.png(已附加)并将OCR的结果输出到myop.txt

enter image description here

现在,我希望这代表this comment(或参见this directly)在多个进程上发生,所以代码在这里:

lst = []
for i in range(4):
    lst.append(subprocess.Popen(['tesseract','mypic.png','myop'+str(i)]))

i=0
l = len(lst)
val = 0 
while(val!=(1<<l)-1):
    if(lst[i].poll() is None):
        print('Waiting for :'+str(i))
        sleep(0.01)
    else:
        temp = val
        val = val or (1<<(i))
        if(val!=temp):
            print('Completed for :'+temp)
    i = (i+1) %l

此代码的作用是对tesseract进行4次调用,将过程对象保存在列表lst中,对所有这些对象进行迭代,直到完成所有所有 。底部给出了无限循环实现的说明。

这里的问题是后一个程序要花费大量时间才能完成。它使用poll()函数None持续等待过程完成,直到过程尚未完成。这本不应该发生的。它应该只花了0.44s多一点的时间。大概不到10分钟!为什么会这样?

我通过深入研究pytesseract来解决此特定错误,当使用multiprocessingpathos并行运行时,这花费了很多时间。因此,这是一个更大版本的缩小版本。我对此有疑问,here


无限循环的解释: val最初为0。当第i个过程完成时,它与2^i进行或运算。因此,如果有3个进程,则如果第一个进程(i = 0)完成,则2^0 = 1val进行或运算为1。第二个和第三个进程完成后,{{ 1}}变成val | 2^0 | 2^1 = 7。2^2也是7。因此,循环一直进行到2^3-1等于val

1 个答案:

答案 0 :(得分:1)

the faq(我强调):

  

Tesseract 4在处理一个页面时还使用多达四个CPU线程,因此单个页面的速度将比Tesseract 3快。

     

如果您的计算机只有两个CPU内核,那么运行四个线程会大大减慢速度,因此最好使用单个线程或最多两个线程! 使用单线程消除了多线程的计算开销,并且通过在每个CPU内核上运行一个Tesseract进程,也是处理大量图像的最佳解决方案。

     

使用环境变量OMP_THREAD_LIMIT设置最大线程数。

     

要禁用多线程,请使用OMP_THREAD_LIMIT = 1。

因此,如果您希望同时运行多个tesseract进程,则可能希望减少(或试验)OMP_THREAD_LIMIT。 最佳值取决于您的计算机可以同时支持多少个线程。

例如,在我的机器上:

import subprocess
import time
import os 

t = time.perf_counter()    
tasks = [('mypic.png', 'myop{}'.format(i)) for i in range(4)]
procs = [subprocess.Popen(['tesseract', infile, outfile], env={'OMP_THREAD_LIMIT':'1'})
         for infile, outfile in tasks]
for proc in procs:
    proc.wait()
print('{} s'.format(time.perf_counter()-t))

在0.220秒内完成,而没有env={'OMP_THREAD_LIMIT':'1'}的相同代码 通常需要3.1到5.1秒之间的时间,每次跑步之间会有很多差异。


要使代码正常工作,请使用binary bitwise or operator, |而不是logical or operator, or

val = val | (1 << (i))

例如,

import time
import subprocess
lst = []
for i in range(4):
    lst.append(subprocess.Popen(['tesseract', 'mypic.png', 'myop'+str(i)]))

i = 0
l = len(lst)
val = 0
counter = 0
while(val != (1 << l)-1):
    if(lst[i].poll() is None):
        time.sleep(0.001)
    else:
        temp = val
        val = val | (1 << (i))
        if(val != temp):
            print('Completed for : {}'.format(i))
    i = (i+1) % l

    counter += 1
print('{} iterations'.format(counter))

打印输出类似

Completed for : 1
Completed for : 2
Completed for : 3
Completed for : 0
6121 iterations

请注意,循环仍会迭代数千次,主要是在lst[i].poll()返回None的时候, 也是因为i = (i+1) % l可以多次访问相同的值。 如果一次迭代花费0.001s,那么6121次迭代将花费6.121s。因此while循环很复杂,而且速度不是很快。