python实现指定目录下批量文件的单词计数:并发版本
在 文章 《python实现指定目录下批量文件的单词计数:串行版本》中, 总体思路是: A. 一次性获取指定目录下的所有符合条件的文件 -> B. 一次性获取所有文件的所有文件行
-> C. 解析所有文件行的单词计数 -> D. 按单词出现次数排序并输出TOPN。 A,B,C,D 是完全串行的
本文实现 并发版本。 并发版本的主要思路是: A. 每次获取一个符合条件的文件 -> B. 获取单个文件的所有文件行 -> C. 解析单个文件的所有单词计数 -> D. 聚合所有单词计数并排序,输出 TOPN。 其中 A,B,C 是并发的,D 如果能够做到动态排序, 后续也可以改成并发的。
一、 线程化改造
首先对串行版本进行线程化改造。 将原来普通类的功能变成线程, 普通类之间的传值调用变为通过队列来传送。代码如下:
#-------------------------------------------------------------------------------
# Name: wordstat_threading.py
# Purpose: statistic words in java files of given directory by threading
#
# Author: qin.shuq
#
# Created: 09/10/2014
# Copyright: (c) qin.shuq 2014
# Licence: <your licence>
#------------------------------------------------------------------------------- import re
import os
import time
import logging
import threading, Queue LOG_LEVELS = {
'DEBUG': logging.DEBUG, 'INFO': logging.INFO,
'WARN': logging.WARNING, 'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL
} def initlog(filename) : logger = logging.getLogger()
hdlr = logging.FileHandler(filename)
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(LOG_LEVELS['INFO']) return logger errlog = initlog("error.log")
infolog = initlog("info.log") timeoutInSecs = 0.05 class FileObtainer(threading.Thread): def __init__(self, dirpath, qOut, threadID, fileFilterFunc=None):
threading.Thread.__init__(self)
self.dirpath = dirpath
self.fileFilterFunc = fileFilterFunc
self.qOut = qOut
self.threadID = threadID
infolog.info('FileObtainer Initialized') def obtainFile(self, path):
fileOrDirs = os.listdir(path)
if len(fileOrDirs) == 0:
return for name in fileOrDirs:
fullPath = path + '/' + name
if os.path.isfile(fullPath):
if self.fileFilterFunc is None:
self.qOut.put(fullPath)
elif self.fileFilterFunc(fullPath):
self.qOut.put(fullPath)
elif os.path.isdir(fullPath):
self.obtainFile(fullPath) def run(self):
print threading.currentThread()
starttime = time.time()
self.obtainFile(self.dirpath)
endtime = time.time()
print 'ObtainFile cost: ', (endtime-starttime)*1000 , 'ms' class WordReading(threading.Thread): def __init__(self, qIn, qOut, threadID):
threading.Thread.__init__(self)
self.qIn = qIn
self.qOut = qOut
self.threadID = threadID
infolog.info('WordReading Initialized') def readFileInternal(self):
lines = []
try:
filename = self.qIn.get(True, timeoutInSecs)
#print filename
except Queue.Empty, emp:
errlog.error('In WordReading:' + str(emp))
return None try:
f = open(filename, 'r')
lines = f.readlines()
infolog.info('[successful read file %s]\n' % filename)
f.close()
except IOError, err:
errorInfo = 'file %s Not found \n' % filename
errlog.error(errorInfo)
return lines def run(self):
print threading.currentThread()
starttime = time.time()
while True:
lines = self.readFileInternal()
if lines is None:
break
self.qOut.put(lines)
endtime = time.time()
print 'WordReading cost: ', (endtime-starttime)*1000 , 'ms' class WordAnalyzing(threading.Thread):
'''
return Map<Word, count> the occurrence times of each word
'''
wordRegex = re.compile("[\w]+") def __init__(self, qIn, threadID):
threading.Thread.__init__(self)
self.qIn = qIn
self.threadID = threadID
self.result = {}
infolog.info('WordAnalyzing Initialized') def run(self):
print threading.currentThread()
starttime = time.time()
lines = []
while True:
try:
start = time.time()
lines = self.qIn.get(True, timeoutInSecs)
except Queue.Empty, emp:
errlog.error('In WordReading:' + str(emp))
break linesContent = ''.join(lines)
matches = WordAnalyzing.wordRegex.findall(linesContent)
if matches:
for word in matches:
if self.result.get(word) is None:
self.result[word] = 0
self.result[word] += 1 endtime = time.time()
print 'WordAnalyzing analyze cost: ', (endtime-starttime)*1000 , 'ms' def obtainResult(self):
return self.result; class PostProcessing(object): def __init__(self, resultMap):
self.resultMap = resultMap def sortByValue(self):
return sorted(self.resultMap.items(),key=lambda e:e[1], reverse=True) def obtainTopN(self, topN):
sortedResult = self.sortByValue()
sortedNum = len(sortedResult)
topN = sortedNum if topN > sortedNum else topN
for i in range(topN):
topi = sortedResult[i]
print topi[0], ' counts: ', topi[1] if __name__ == "__main__": dirpath = "c:\\Users\\qin.shuq\\Desktop\\region_master\\src"
if not os.path.exists(dirpath):
print 'dir %s not found.' % dirpath
exit(1) qFile = Queue.Queue()
qLines = Queue.Queue() fileObtainer = FileObtainer(dirpath, qFile, "Thread-FileObtainer", lambda f: f.endswith('.java'))
wr = WordReading(qFile, qLines, "Thread-WordReading")
wa = WordAnalyzing(qLines, "Thread-WordAnalyzing") fileObtainer.start()
wr.start()
wa.start() wa.join() starttime = time.time()
postproc = PostProcessing(wa.obtainResult())
postproc.obtainTopN(30)
endtime = time.time()
print 'PostProcessing cost: ', (endtime-starttime)*1000 , 'ms' print 'exit the program.'
测量时间:
$ time python wordstat_serial.py
ObtainFile cost: 92.0000076294 ms
WordReading cost: 504.00018692 ms
WordAnalyzing cost: 349.999904633 ms
PostProcessing cost: 16.0000324249 ms
real 0m1.100s
user 0m0.000s
sys 0m0.046s $ time python wordstat_threading.py
ObtainFile cost: 402.99987793 ms
WordReading cost: 1477.99992561 ms
WordAnalyzing analyze cost: 1528.00011635 ms
PostProcessing cost: 16.0000324249 ms real 0m1.690s
user 0m0.000s
sys 0m0.046s
从时间测量的结果来看,并发版本甚至还不如串行版本, 这主要是读取文件还是单线程的, 同时队列之间传送消息是阻塞式的,会耗费一定时间。此外, 并发版本尚未使用到多核优势, 也是后续改进点。
注意到 WordAnalyzing 与 WordReading 所耗费的时间很接近,这表明两者是并发执行的 。 PostProcessing 耗费时间几乎可以忽略, 暂不做优化。 下一步优化工作是 ObtainFile 和 WordReading.
二、 使用多线程和多进程优化
1. 由于 Queue.put 会耗费一定时间(平均1ms左右),因此, 大量文件名称的put必定耗费很多不必要的时间, 改进版本中使用文件列表,减少put次数;
2. WordReading 采用多线程来读取大量文件;
3. WordAnalyzing 采用多进程来进行单词计数。
经过优化后的 WordReading 和 WordAnalyzing 耗费时间基本上与串行版本相同。 瓶颈在 FileObtainer
上。 目前对os.walk, for 循环进行了测量, 但测量时间总是远小于ObtainFile cost, 尚没有找出究竟耗费时间在哪里了。
#-------------------------------------------------------------------------------
# Name: wordstat_threading_improved.py
# Purpose: statistic words in java files of given directory by threading
# improved
#
# Author: qin.shuq
#
# Created: 09/10/2014
# Copyright: (c) qin.shuq 2014
# Licence: <your licence>
#------------------------------------------------------------------------------- import re
import os
import time
import logging
import threading, Queue
from multiprocessing import Process, Pool, cpu_count LOG_LEVELS = {
'DEBUG': logging.DEBUG, 'INFO': logging.INFO,
'WARN': logging.WARNING, 'ERROR': logging.ERROR,
'CRITICAL': logging.CRITICAL
} def initlog(filename) : logger = logging.getLogger()
hdlr = logging.FileHandler(filename)
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(LOG_LEVELS['INFO']) return logger errlog = initlog("error.log")
infolog = initlog("info.log") timeoutInSecs = 0.1 class FileObtainer(threading.Thread): def __init__(self, dirpath, qOut, threadID, fileFilterFunc=None):
threading.Thread.__init__(self)
self.dirpath = dirpath
self.fileFilterFunc = fileFilterFunc
self.qOut = qOut
self.threadID = threadID
infolog.info('FileObtainer Initialized') def run(self):
print threading.currentThread()
starttime = time.time() for path, dirs, filenames in os.walk(self.dirpath):
if len(filenames) > 0:
files = []
for filename in filenames:
start = time.time()
fullPath = path+'/'+filename
files.append(fullPath)
end = time.time() if self.fileFilterFunc is None:
self.qOut.put_nowait(files)
else:
fileterFiles = filter(self.fileFilterFunc, files)
if len(fileterFiles) > 0:
self.qOut.put_nowait(fileterFiles) endtime = time.time()
print 'ObtainFile cost: ', (endtime-starttime)*1000 , 'ms' def readFile(filename, qOut):
try:
f = open(filename, 'r')
lines = f.readlines()
infolog.info('[successful read file %s]\n' % filename)
f.close()
except IOError, err:
errorInfo = 'file %s Not found \n' % filename
errlog.error(errorInfo)
qOut.put(lines) class WordReading(threading.Thread): def __init__(self, qIn, qOut, threadID):
threading.Thread.__init__(self)
self.qIn = qIn
self.qOut = qOut
self.threadID = threadID
self.threads = []
infolog.info('WordReading Initialized') def readFileInternal(self):
try:
filelist = self.qIn.get(True, timeoutInSecs)
for filename in filelist:
t = threading.Thread(target=readFile, args=(filename, self.qOut), name=self.threadID+'-'+filename)
t.start()
self.threads.append(t)
return []
except Queue.Empty, emp:
errlog.error('In WordReading:' + str(emp))
return None def run(self):
print threading.currentThread()
starttime = time.time()
while True:
lines = self.readFileInternal()
if lines is None:
break for t in self.threads:
t.join() endtime = time.time()
print 'WordReading cost: ', (endtime-starttime)*1000 , 'ms' def processLines(lines):
result = {}
linesContent = ''.join(lines)
matches = WordAnalyzing.wordRegex.findall(linesContent)
if matches:
for word in matches:
if result.get(word) is None:
result[word] = 0
result[word] += 1
return result def mergeToSrcMap(srcMap, destMap):
for key, value in destMap.iteritems():
if srcMap.get(key):
srcMap[key] = srcMap.get(key)+destMap.get(key)
else:
srcMap[key] = destMap.get(key)
return srcMap class WordAnalyzing(threading.Thread):
'''
return Map<Word, count> the occurrence times of each word
'''
wordRegex = re.compile("[\w]+") def __init__(self, qIn, threadID):
threading.Thread.__init__(self)
self.qIn = qIn
self.threadID = threadID
self.resultMap = {}
self.pool = Pool(cpu_count())
infolog.info('WordAnalyzing Initialized') def run(self):
print threading.currentThread()
starttime = time.time()
lines = []
futureResult = []
while True:
try:
lines = self.qIn.get(True, timeoutInSecs)
futureResult.append(self.pool.apply_async(processLines, args=(lines,)))
except Queue.Empty, emp:
errlog.error('In WordReading:' + str(emp))
break self.pool.close()
self.pool.join() resultMap = {}
for res in futureResult:
mergeToSrcMap(self.resultMap, res.get())
endtime = time.time()
print 'WordAnalyzing analyze cost: ', (endtime-starttime)*1000 , 'ms' def obtainResult(self):
#print len(self.resultMap)
return self.resultMap class PostProcessing(object): def __init__(self, resultMap):
self.resultMap = resultMap def sortByValue(self):
return sorted(self.resultMap.items(),key=lambda e:e[1], reverse=True) def obtainTopN(self, topN):
sortedResult = self.sortByValue()
sortedNum = len(sortedResult)
topN = sortedNum if topN > sortedNum else topN
for i in range(topN):
topi = sortedResult[i]
print topi[0], ' counts: ', topi[1] if __name__ == "__main__": dirpath = "E:\\workspace\\java\\javastudy\\src"
if not os.path.exists(dirpath):
print 'dir %s not found.' % dirpath
exit(1) qFile = Queue.Queue()
qLines = Queue.Queue() fileObtainer = FileObtainer(dirpath, qFile, "Thread-FileObtainer", lambda f: f.endswith('.java'))
wr = WordReading(qFile, qLines, "Thread-WordReading")
wa = WordAnalyzing(qLines, "Thread-WordAnalyzing") fileObtainer.start()
wr.start()
wa.start() wa.join() starttime = time.time()
postproc = PostProcessing(wa.obtainResult())
postproc.obtainTopN(30)
endtime = time.time()
print 'PostProcessing cost: ', (endtime-starttime)*1000 , 'ms' print 'exit the program.'
【未完待续】
python实现指定目录下批量文件的单词计数:并发版本的更多相关文章
- python实现指定目录下批量文件的单词计数:串行版本
直接上代码. 练习目标: 1. 使用 Python 面向对象的方法封装逻辑和表达 : 2. 使用异常处理和日志API : 3. 使用文件目录读写API : 4. 使用 list, map, t ...
- [python] 在指定目录下找文件
import os # 查找当前目录下所有包含关键字的文件 def findFile(path, filekw): return[os.path.join(path,x) for x in os.li ...
- python查找指定目录下所有文件,以及改文件名的方法
一: os.listdir(path) 把path目录下的所有文件保存在列表中: >>> import os>>> import re>>> pa ...
- python实现指定目录下JAVA文件单词计数的多进程版本
要说明的是, 串行版本足够快了, 在我的酷睿双核 debian7.6 下运行只要 0.2s , 简直是难以超越. 多进程版本难以避免大量的进程创建和数据同步与传输开销, 性能反而不如串行版本, 只能作 ...
- PHP 批量获取指定目录下的文件列表(递归,穿透所有子目录)
//调用 $dir = '/Users/xxx/www'; $exceptFolders = array('view','test'); $exceptFiles = array('BaseContr ...
- python获取指定目录下所有文件名os.walk和os.listdir
python获取指定目录下所有文件名os.walk和os.listdir 觉得有用的话,欢迎一起讨论相互学习~Follow Me os.walk 返回指定路径下所有文件和子文件夹中所有文件列表 其中文 ...
- Python获取指定目录下所有子目录、所有文件名
需求 给出制定目录,通过Python获取指定目录下的所有子目录,所有(子目录下)文件名: 实现 import os def file_name(file_dir): for root, dirs, f ...
- PHP 获取指定目录下所有文件(包含子目录)
PHP 获取指定目录下所有文件(包含子目录) //glob — 寻找与模式匹配的文件路径 $filter_dir = array('CVS', 'templates_c', 'log', 'img', ...
- iOS案例:读取指定目录下的文件列表
// // main.m // 读取指定目录下的文件列表 // // Created by Apple on 15/11/24. // Copyright © 2015年 Apple. All rig ...
随机推荐
- vue中的小踩坑(01)
前言: 昨天算是使用vue2.0+element-ui做了一点小小的页面,可是源于其中遇到的问题,特地整理一下,以防自己还有其他的小伙伴们继续踩坑. 过程: 1.不知道大家有没有注意到 ...
- CentOS6.4配置163的yum源
CentOS系统自带的更新源的速度实在是慢,为了让CentOS6使用速度更快的YUM更新源,可以选择163(网易)的更新源. 1.下载repo文件 wget http://mirrors.163.co ...
- BSTR与char*、cstring、CComBSTR的转换
// BSTR_Convert.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include <comutil.h> // ...
- 是否可以从一个static(静态)方法内部调用非static(非静态)方法?
不可以.static方法调用时不需要创建对象(可直接调用),当一个static方法被调用时,可能还没有创建任何实例对象,也就不可能调用非静态方法.
- poj_1037 动态规划+字典序第k大
题目大意 给定n个数字,规定一种 cute 排序:序列中的数字大小为严格的波浪形,即 a[0] > a[1] < a[2] > a[3] < .... 或者 a[0] < ...
- c/c++左值和右值
C/C++中的变量有左值和右值之分,他们的区别主要如下: (1)左值可以放在赋值号 = 的左右两边,右值只能放在赋值号 = 的右边 (2)在C语言中,有名字的变量即为左值:而函数的运行结果或表达式中间 ...
- Java三方---->Thumbnailator框架的使用
Thumbnailator是一个用来生成图像缩略图的 Java类库,通过很简单的代码即可生成图片缩略图,也可直接对一整个目录的图片生成缩略图.有了它我们就不用在费心思使用Image I/O API,J ...
- JS-鼠标彩色拖尾小效果
实现步骤解析: * 这原本就是一个鼠标后面跟随一串小方块的效果, * 后来我就想,运用之前学的随机数的案例把小方块的颜色做成彩色的, * 并且每一个小方块的色彩是随机分配而不是我自己手 ...
- 窗口大小改变绑定resize事件
当为窗口绑定resize事件时,大部分浏览器会在每改变一个像素就触发一次resize事件.这严重影响了网站的性能. 解决方案是:利用settimeout方法为事件发触发的方法设置延迟执行的时间. 实现 ...
- Android 内存使用hprof文件打开方法
http://blog.csdn.net/moruihong/article/details/7677128 与C++的内存不同,C++的内存泄露是由于分配了内存给某程序但是又没有回收造成的.Java ...