虽然查看线程和队列会很有趣,所以我编写了2个脚本,一个会打破文件并加密一个线程中的每个块,另一个会串行完成。我仍然是python的新手,并不知道为什么踩踏脚本需要这么长时间。
螺纹脚本:
#!/usr/bin/env python
from Crypto.Cipher import AES
from optparse import OptionParser
import os, base64, time, sys, hashlib, pickle, threading, timeit, Queue
BLOCK_SIZE = 32 #32 = 256-bit | 16 = 128-bit
TFILE = 'mytestfile.bin'
CHUNK_SIZE = 2048 * 2048
KEY = os.urandom(32)
class DataSplit():
def __init__(self,fileObj, chunkSize):
self.fileObj = fileObj
self.chunkSize = chunkSize
def split(self):
while True:
data = self.fileObj.read(self.chunkSize)
if not data:
break
yield data
class encThread(threading.Thread):
def __init__(self, seg_queue,result_queue, cipher):
threading.Thread.__init__(self)
self.seg_queue = seg_queue
self.result_queue = result_queue
self.cipher = cipher
def run(self):
while True:
#Grab a data segment from the queue
data = self.seg_queue.get()
encSegment = []
for lines in data:
encSegment.append(self.cipher.encrypt(lines))
self.result_queue.put(encSegment)
print "Segment Encrypted"
self.seg_queue.task_done()
start = time.time()
def main():
seg_queue = Queue.Queue()
result_queue = Queue.Queue()
estSegCount = (os.path.getsize(TFILE)/CHUNK_SIZE)+1
cipher = AES.new(KEY, AES.MODE_CFB)
#Spawn threads (one for each segment at the moment)
for i in range(estSegCount):
eT = encThread(seg_queue, result_queue, cipher)
eT.setDaemon(True)
eT.start()
print ("thread spawned")
fileObj = open(TFILE, "rb")
splitter = DataSplit(fileObj, CHUNK_SIZE)
for data in splitter.split():
seg_queue.put(data)
print ("Data sent to thread")
seg_queue.join()
#result_queue.join()
print ("Seg Q: {0}".format(seg_queue.qsize()))
print ("Res Q: {0}".format(result_queue.qsize()))
main()
print ("Elapsed Time: {0}".format(time.time()-start))
串行脚本:
#!/usr/bin/env python
from Crypto.Cipher import AES
from optparse import OptionParser
import os, base64, time, sys, hashlib, pickle, threading, timeit, Queue
TFILE = 'mytestfile.bin'
CHUNK_SIZE = 2048 * 2048
class EncSeries():
def __init(self):
pass
def loadFile(self,path):
openFile = open(path, "rb")
#fileData = openFile.readlines()
fileData = openFile.read(CHUNK_SIZE)
openFile.close()
return fileData
def encryptData(self,key, data):
cipher = AES.new(key, AES.MODE_CFB)
newData = []
for lines in data:
newData.append(cipher.encrypt(lines))
return newData
start = time.time()
def main():
print ("Start")
key = os.urandom(32)
run = EncSeries()
fileData = run.loadFile(TFILE)
encFileData=run.encryptData(key, fileData)
print("Finish")
main()
print ("Elapsed Time: {0}".format(time.time()-start))
使用readlines()而不是read也似乎在串行版本上大大加快了速度,但它已经比线程版本快得多。
答案 0 :(得分:1)
线程不是加速程序的神奇方法 - 将工作分成线程通常会减慢速度,除非程序花费大量时间等待I / O.每个新线程在分割工作时会增加代码的开销,并且在线程之间切换时会增加OS中的开销。
理论上,如果你在多处理器CPU上运行,那么线程可以在不同的处理器上运行,因此工作是并行完成的,但即便如此,没有必要拥有比处理器更多的线程。
在实践中它是完全不同的,至少对于C版的Python来说。使用多个处理器时,GIL根本无法正常工作。请参阅David Beazley的presentation了解原因。 IronPython和Jython没有这个问题。
如果你真的想要并行化工作,那么最好产生多个进程并将工作分配给它们,但是传递大块数据的进程间通信开销可能会抵消任何好处并行性。
答案 1 :(得分:1)
看起来你的第二个版本只读取一个块,而第一个版本读取整个文件 - 这可以解释大的加速。 编辑:另一个问题:我刚刚注意到您无缘无故地运行for lines in data
- 这实际上会单独加密字符,这要慢得多。相反,只需将数据直接传递给encrypt
。
启动比CPU更重的线程没有意义。
如果线程调用的是在运行时解锁GIL的扩展模块,则它们只能并行工作。我不认为PyCrypto这样做,所以你不会在这里完成任何并行工作。
如果瓶颈是磁盘性能,那么无论如何你都不会看到很多改进 - 在这种情况下,最好让一个线程执行磁盘I / O而另一个线程执行加密。 GIL不会成为问题,因为它在执行磁盘I / O时被释放。
答案 2 :(得分:1)
我观看了Dave Kirby链接到的演示文稿并尝试了一个示例计数器,这个计数器需要两倍的时间来运行两个线程:
import time
from threading import Thread
countmax=100000000
def count(n):
while n>0:
n-=1
def main1():
count(countmax)
count(countmax)
def main2():
t1=Thread(target=count,args=(countmax,))
t2=Thread(target=count,args=(countmax,))
t1.start()
t2.start()
t1.join()
t2.join()
def timeit(func):
start = time.time()
func()
end=time.time()-start
print ("Elapsed Time: {0}".format(end))
if __name__ == '__main__':
timeit(main1)
timeit(main2)
输出:
Elapsed Time: 21.5470001698
Elapsed Time: 55.3279998302
但是,如果我更改Thread for Process:
from multiprocessing import Process
和
t1=Process(target ....
等。我得到了这个输出:
Elapsed Time: 20.5
Elapsed Time: 10.4059998989
现在好像我的Pentium CPU有两个内核,我打赌它是超线程。任何人都可以在他们的两个或四个核心机器上尝试这个并运行2或4个线程吗?
的python 2.6.4文档答案 3 :(得分:0)
线程有几种不同的用途:
他们只提供加速,如果它们允许您在问题的同时使多个硬件同时工作,无论该硬件是CPU核心还是磁盘头。
它们允许您跟踪多个I / O事件序列,如果没有它们会更加复杂,例如与多个用户同时进行对话。
后者不是为了提高性能,而是为了清晰的代码。
答案 4 :(得分:0)
更新这个帖子只是一个简单的注释:python 3.2有一个新的GIL实现,它减轻了与多线程相关的大量开销,但没有消除锁定。 (即它不允许您使用多个核心,但它允许您有效地在该核心上使用多个线程。)