我有一个包含大量数据的文件。每行都是一条记录。我试图对整个文件做一些ETL工作。现在我正在使用标准输入逐行读取数据。关于这一点很酷的是你的脚本可以非常灵活地与其他脚本和shell命令集成。我将结果写入标准输出。例如。
$ cat input_file
line1
line2
line3
line4
...
我当前的python代码如下所示 - parse.py
import sys
for line in sys.stdin:
result = ETL(line) # ETL is some self defined function which takes a while to execute.
print result
下面的代码就是它现在的工作方式:
cat input_file | python parse.py > output_file
我已经看过Python的Threading模块,我想知道如果我使用该模块,性能是否会得到显着改善。
问题1: 我应该如何规划每个帖子的配额,为什么?
...
counter = 0
buffer = []
for line in sys.stdin:
buffer.append(line)
if counter % 5 == 0: # maybe assign 5 rows to each thread? if not, is there a rule of thumb to determine
counter = 0
thread = parser(buffer)
buffer = []
thread.start()
问题2: 多个线程可能会同时将结果打印回stdout,如何组织它们并避免下面的情况?
import threading
import time
class parser(threading.Thread):
def __init__ (self, data_input):
threading.Thread.__init__(self)
self.data_input = data_input
def run(self):
for elem in self.data_input:
time.sleep(3)
print elem + 'Finished'
work = ['a', 'b', 'c', 'd', 'e', 'f']
thread1 = parser(['a', 'b'])
thread2 = parser(['c', 'd'])
thread3 = parser(['e', 'f'])
thread1.start()
thread2.start()
thread3.start()
输出非常难看,其中一行包含两个线程的输出。
aFinished
cFinishedeFinished
bFinished
fFinished
dFinished
答案 0 :(得分:4)
首先提出第二个问题,这就是mutexes的用途。您可以通过使用锁来协调解析器之间来获得所需的更清晰的输出,并确保在给定的时间段内只有一个线程可以访问输出流:
class parser(threading.Thread):
output_lock = threading.Lock()
def __init__ (self, data_input):
threading.Thread.__init__(self)
self.data_input = data_input
def run(self):
for elem in self.data_input:
time.sleep(3)
with self.output_lock:
print elem + 'Finished'
关于您的第一个问题,请注意,多线程可能无法为您的特定工作负载带来任何好处。这在很大程度上取决于您对每个输入行(您的ETL
函数)所做的工作是主要是CPU绑定还是IO绑定。如果是前者(我怀疑可能),由于global interpreter lock,线程将无济于事。在这种情况下,您可能希望使用multiprocessing
模块在多个进程之间分配工作而不是多个线程。
但是您可以通过更容易实现的工作流程获得相同的结果:将输入文件拆分为n
个(使用例如split
命令);在每个子文件上分别调用extract-and-transform脚本;然后连接生成的输出文件。
一个挑剔:“使用标准输入逐行读取数据,因为它不会将整个文件加载到内存中”会产生误解。您可以在Python中逐行读取文件,例如,使用以下构造中的文件对象替换sys.stdin
:
for line in sys.stdin:
另请参阅文件对象的readline()
方法,并注意read()
可以将要读取的最大字节数作为参数。
答案 1 :(得分:0)
线程是否有用,您在很大程度上取决于您的情况。特别是,如果您的ETL()
函数涉及大量磁盘访问,那么线程化可能会显着提高您的速度。
在回答你的第一个问题时,我总是发现它只是取决于你。在确定理想的线程数时,有许多因素在起作用,其中许多因素与程序有关。例如,如果您正在进行大量磁盘访问(这非常慢),那么您将需要更多线程在等待磁盘访问时利用停机时间。但是,如果该程序受CPU限制,那么大量的线程可能不会非常有用。因此,尽管可以分析所有因素以得出理想数量的线程,但通常可以更快地进行初始猜测,然后从那里进行调整。
更具体地说,为每个线程分配一定数量的行可能不是分配工作的最佳方式。例如,考虑一行是否需要特别长的时间来处理。最好是一个线程可以在那一行上工作,而其他线程可以在此期间再做几行。处理此问题的最佳方法是使用队列。如果将每一行推入队列,则每个线程都可以从队列中拉出一行,处理它,然后重复,直到队列为空。通过这种方式,工作得以分配,使得没有任何线程无法完成工作(当然,直到最后)。
现在,第二个问题。你肯定从多个线程写入stdout并不是一个理想的解决方案。理想情况下,您可以安排一些事情,以便只在一个地方写入stdout。一个很好的方法是使用队列。如果你让每个线程将其输出写入共享队列,那么你可以产生一个额外的线程,其唯一的任务是将项目拉出该队列并将它们打印到stdout。通过将打印限制为仅一个线程,您将避免多个线程尝试一次打印时固有的问题。