当前位置: 首页 > 知识库问答 >
问题:

多个子过程需要大量的时间来完成

范修伟
2023-03-14

我有一个使用子流程模块的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。

现在,我希望这在多个进程上发生,代表这个注释(或者直接看到这个),所以代码在这里:

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

这段代码所做的是调用4次tesseract,将流程对象保存在列表lst中,遍历所有这些对象,直到它们全部完成。下面给出了无限循环实现的说明。

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

我通过深入pytesseract找到了这个特定的错误,当使用多重处理悲情并行运行时,这需要很多时间。所以这是一个更大问题的缩小版本。我的问题可以在这里找到。

无限循环的解释:val最初为0。当第i个过程完成时,它与2^i进行OR运算。因此,如果有3个过程,那么如果第一个过程(i=0)完成,则2^0=1将使用val将其设为1。第二个和第三个过程完成后,val变为2^02^12^2=7。而2^3-1也是7。因此循环一直工作到val等于2^{number of processs}-1

共有1个答案

傅旺
2023-03-14

根据常见问题解答(我的重点):

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

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

使用环境变量OMP\u THREAD\u LIMIT设置最大线程数。

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

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

例如,在我的机器上:

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秒,运行之间有很多变化。

要使代码正常工作,请使用二进制位or运算符|而不是逻辑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]。轮询()返回,但也是因为i=(i 1)%l可以多次重访相同的值。如果一次迭代需要0.001秒,那么6121次迭代将需要6.121秒。所以循环很复杂,速度也不是很快。

 类似资料:
  • 我正在使用以下代码: xlsx文件本身有25,000行,每行有500列的内容。在调试过程中,我看到我创建XSSFWorkbook的第三行,需要很多时间(1小时!)来完成这个语句。 有没有更好的方法来访问原始xlsx文件的值?

  • 我正在运行一个简单的MySQL查询来查找用户玩游戏的总时间: EXPLAIN显示了以下内容: 重要的是,该表使用InnoDB引擎并限制了大约200万条记录。

  • 我正在用CXF开发一个SOAP客户端。我有WSDL2Java生成的代码。我做了一个JAR可执行文件来测试。当我尝试提出请求时,我首先这样做: WSDL是web服务的URL。 service_name是服务的QName。 MintService是从wsdl2java代码生成的类。 MintServiceSOAP是endpoint的QName MintService.class是WSLD2Java生成

  • 我在Redis git hub上发布了这个问题,如果我看到任何回复,我会更新双方。 在VisualStudio 2015和x64上运行C 我注意到调用“get”几乎需要2秒钟才能返回一个值。我的钥匙是“控制:107:1”;我在本地机器上运行Redis服务器;它有大约200把钥匙。 我甚至解构了这个命令:redis_客户端- 我的结果是: redis_client- 收到1939年 val.as_s

  • 我正在努力改进我写的一个数据传输程序。我在寻找如何让它更快的建议。我的程序通过填充ResultSet并将结果写入文件来从数据库(通常是Oracle11g)中提取数据。该程序定期查看表,并查询某个特殊列是否发生了更改。例如,这可能是这样一个查询:

  • 问题内容: 有没有办法知道在Java中执行一个循环要花多少秒? 例如: 它不必精确到100%,而只是想知道要花费多长时间。里面的算法是一种写入.txt文件的密钥生成器。我希望它花费几分钟,因此对于我的第一次测试,我想计算秒数。 问题答案: 用Java编写微基准测试时需要非常小心。例如: 如果JIT编译器可以弄清楚循环体不影响代码结果,则可以对其进行优化。例如: 可能很快就会“运行”。 JIT编译后