Python - 多线程字/行计数

时间:2012-06-03 07:47:57

标签: python mapreduce word-count

我正试图在python中处理多线程。我有工作代码来计算单词数,带有文本的行数,并创建一个带有每个单词计数的字典。它可以在代码注释中提到的小文件上快速运行。但是我通常使用glob来拉入多个文件。当我这样做时,我的运行时间显着增加。同时,由于我的脚本是单线程的,我看到我有3个其他核心闲置而一个最大化。

我以为我会给pythons多线程模块一个镜头,这是我到目前为止所做的(非工作):

#!/bin/python
#
# test file: http://www.gutenberg.org/ebooks/2852.txt.utf-8

import fileinput
from collections import defaultdict
import threading
import time

inputfilename = 'pg2852.txt'

exitFlag = 0

line = []
line_counter = 0
tot_words = 0
word_dict = defaultdict(int)

def myCounters( threadName, delay):
        for line in fileinput.input([inputfilename]):
                line = line.strip();
                if not line: continue
                words = line.split()
                tot_words += len(words)
                line_counter += 1
                for word in words:
                        word_dict[word] += 1

        print "%s: %s:" %( threadName, time.ctime(time.time()) )
        print word_dict
        print "Total Words: ", tot_words
        print "Total Lines: ", line_counter

try:
        thread.start_new_thread( myCounters, ("Thread-1", 2, ) )
        thread.start_new_thread( myCounters, ("Thread-2", 4, ) )
except:
        print "Error: Thread Not Started"

while 1:
        pass

对于那些尝试此代码的人来说,它不起作用。我假设我需要将输入文件分成块并以某种方式合并输出。 ? map / reduce?也许有一个更简单的解决方案?

编辑:

可能是这样的:

  1. 打开文件,
  2. 将其分成块
  3. 将每个块提供给不同的线程
  4. 获取计数并在每个块上构建字典
  5. merge counts / dict
  6. 返回结果

1 个答案:

答案 0 :(得分:5)

首先,你是正确的,你需要使用并行进程而不是并行线程。由于Global Interpreter Lock(GIL),执行此类任务 [参见下面的ETA]将无法很好地扩展到python下的多个线程。

如果要并行处理单个文件,显而易见的方法是首先检查文件大小,然后将相等大小的块分配给多个进程。这只会涉及告诉每个进程从文件中的哪个位置开始,以及结束的位置。 (当然,你必须要小心不要计算任何单词或行两次。一个简单的方法是让每个进程忽略初始字节,直到它到达行的开头,然后开始计数)。

但是,您在问题中说明您将使用glob来处理多个文件。因此,不是采用复杂的分块文件路径并将块分配给不同的进程,更简单的选择就是将不同的文件分配给不同的进程。


<强> ETA:

在Python中使用线程适用于某些用例,例如使用长时间阻塞的I / O函数。 @uselpa是正确的,如果处理是I / O绑定,那么线程可能表现良好,但这不是这里的情况,因为瓶颈实际上是解析,而不是文件I / O.这是由于Python作为解释语言的性能特征;在编译语言中,I / O更可能成为瓶颈。

我提出这些说法是因为我刚刚根据原始代码进行了一些测量(使用包含100个pg2852.txt连接副本的测试文件):

  • 作为单个线程运行需要大约2.6秒来读取和解析文件,但是当我注释掉解析代码时只需0.2秒。
  • 并行运行两个线程(从同一个文件读取)需要7.2s,但并行启动的两个单线程进程只需3.3s就可以 完成。