• python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比


    python3下multiprocessing、threading和gevent性能对比----暨进程池、线程池和协程池性能对比

     

            目前计算机程序一般会遇到两类I/O:硬盘I/O和网络I/O。我就针对网络I/O的场景分析下python3下进程、线程、协程效率的对比。进程采用multiprocessing.Pool进程池,线程是自己封装的进程池,协程采用gevent的库。用python3自带的urlllib.request和开源的requests做对比。代码如下:

    [python] view plain copy
     
    1. import urllib.request  
    2. import requests  
    3. import time  
    4. import multiprocessing  
    5. import threading  
    6. import queue  
    7.   
    8. def startTimer():  
    9.     return time.time()  
    10.   
    11. def ticT(startTime):  
    12.     useTime = time.time() - startTime  
    13.     return round(useTime, 3)  
    14.   
    15. #def tic(startTime, name):  
    16. #    useTime = time.time() - startTime  
    17. #    print('[%s] use time: %1.3f' % (name, useTime))  
    18.   
    19. def download_urllib(url):  
    20.     req = urllib.request.Request(url,  
    21.             headers={'user-agent': 'Mozilla/5.0'})  
    22.     res = urllib.request.urlopen(req)  
    23.     data = res.read()  
    24.     try:  
    25.         data = data.decode('gbk')  
    26.     except UnicodeDecodeError:  
    27.         data = data.decode('utf8', 'ignore')  
    28.     return res.status, data  
    29.   
    30. def download_requests(url):  
    31.     req = requests.get(url,  
    32.             headers={'user-agent': 'Mozilla/5.0'})  
    33.     return req.status_code, req.text  
    34.   
    35. class threadPoolManager:  
    36.     def __init__(self,urls, workNum=10000,threadNum=20):  
    37.         self.workQueue=queue.Queue()  
    38.         self.threadPool=[]  
    39.         self.__initWorkQueue(urls)  
    40.         self.__initThreadPool(threadNum)  
    41.   
    42.     def __initWorkQueue(self,urls):  
    43.         for i in urls:  
    44.             self.workQueue.put((download_requests,i))  
    45.   
    46.     def __initThreadPool(self,threadNum):  
    47.         for i in range(threadNum):  
    48.             self.threadPool.append(work(self.workQueue))  
    49.   
    50.     def waitAllComplete(self):  
    51.         for i in self.threadPool:  
    52.             if i.isAlive():  
    53.                 i.join()  
    54.   
    55. class work(threading.Thread):  
    56.     def __init__(self,workQueue):  
    57.         threading.Thread.__init__(self)  
    58.         self.workQueue=workQueue  
    59.         self.start()  
    60.     def run(self):  
    61.         while True:  
    62.             if self.workQueue.qsize():  
    63.                 do,args=self.workQueue.get(block=False)  
    64.                 do(args)  
    65.                 self.workQueue.task_done()  
    66.             else:  
    67.                 break  
    68.   
    69. urls = ['http://www.ustchacker.com'] * 10  
    70. urllibL = []  
    71. requestsL = []  
    72. multiPool = []  
    73. threadPool = []  
    74. N = 20  
    75. PoolNum = 100  
    76.   
    77. for i in range(N):  
    78.     print('start %d try' % i)  
    79.     urllibT = startTimer()  
    80.     jobs = [download_urllib(url) for url in urls]  
    81.     #for status, data in jobs:  
    82.     #    print(status, data[:10])  
    83.     #tic(urllibT, 'urllib.request')  
    84.     urllibL.append(ticT(urllibT))  
    85.     print('1')  
    86.       
    87.     requestsT = startTimer()  
    88.     jobs = [download_requests(url) for url in urls]  
    89.     #for status, data in jobs:  
    90.     #    print(status, data[:10])  
    91.     #tic(requestsT, 'requests')  
    92.     requestsL.append(ticT(requestsT))  
    93.     print('2')  
    94.       
    95.     requestsT = startTimer()  
    96.     pool = multiprocessing.Pool(PoolNum)  
    97.     data = pool.map(download_requests, urls)  
    98.     pool.close()  
    99.     pool.join()  
    100.     multiPool.append(ticT(requestsT))  
    101.     print('3')  
    102.   
    103.     requestsT = startTimer()  
    104.     pool = threadPoolManager(urls, threadNum=PoolNum)  
    105.     pool.waitAllComplete()  
    106.     threadPool.append(ticT(requestsT))  
    107.     print('4')  
    108.   
    109. import matplotlib.pyplot as plt  
    110. x = list(range(1, N+1))  
    111. plt.plot(x, urllibL, label='urllib')  
    112. plt.plot(x, requestsL, label='requests')  
    113. plt.plot(x, multiPool, label='requests MultiPool')  
    114. plt.plot(x, threadPool, label='requests threadPool')  
    115. plt.xlabel('test number')  
    116. plt.ylabel('time(s)')  
    117. plt.legend()  
    118. plt.show()  

    运行结果如下:

            从上图可以看出,python3自带的urllib.request效率还是不如开源的requests,multiprocessing进程池效率明显提升,但还低于自己封装的线程池,有一部分原因是创建、调度进程的开销比创建线程高(测试程序中我把创建的代价也包括在里面)。

            下面是gevent的测试代码:

    [python] view plain copy
     
    1. import urllib.request  
    2. import requests  
    3. import time  
    4. import gevent.pool  
    5. import gevent.monkey  
    6.   
    7. gevent.monkey.patch_all()  
    8.   
    9. def startTimer():  
    10.     return time.time()  
    11.   
    12. def ticT(startTime):  
    13.     useTime = time.time() - startTime  
    14.     return round(useTime, 3)  
    15.   
    16. #def tic(startTime, name):  
    17. #    useTime = time.time() - startTime  
    18. #    print('[%s] use time: %1.3f' % (name, useTime))  
    19.   
    20. def download_urllib(url):  
    21.     req = urllib.request.Request(url,  
    22.             headers={'user-agent': 'Mozilla/5.0'})  
    23.     res = urllib.request.urlopen(req)  
    24.     data = res.read()  
    25.     try:  
    26.         data = data.decode('gbk')  
    27.     except UnicodeDecodeError:  
    28.         data = data.decode('utf8', 'ignore')  
    29.     return res.status, data  
    30.   
    31. def download_requests(url):  
    32.     req = requests.get(url,  
    33.             headers={'user-agent': 'Mozilla/5.0'})  
    34.     return req.status_code, req.text  
    35.   
    36. urls = ['http://www.ustchacker.com'] * 10  
    37. urllibL = []  
    38. requestsL = []  
    39. reqPool = []  
    40. reqSpawn = []  
    41. N = 20  
    42. PoolNum = 100  
    43.   
    44. for i in range(N):  
    45.     print('start %d try' % i)  
    46.     urllibT = startTimer()  
    47.     jobs = [download_urllib(url) for url in urls]  
    48.     #for status, data in jobs:  
    49.     #    print(status, data[:10])  
    50.     #tic(urllibT, 'urllib.request')  
    51.     urllibL.append(ticT(urllibT))  
    52.     print('1')  
    53.       
    54.     requestsT = startTimer()  
    55.     jobs = [download_requests(url) for url in urls]  
    56.     #for status, data in jobs:  
    57.     #    print(status, data[:10])  
    58.     #tic(requestsT, 'requests')  
    59.     requestsL.append(ticT(requestsT))  
    60.     print('2')  
    61.       
    62.     requestsT = startTimer()  
    63.     pool = gevent.pool.Pool(PoolNum)  
    64.     data = pool.map(download_requests, urls)  
    65.     #for status, text in data:  
    66.     #    print(status, text[:10])  
    67.     #tic(requestsT, 'requests with gevent.pool')  
    68.     reqPool.append(ticT(requestsT))  
    69.     print('3')  
    70.       
    71.     requestsT = startTimer()  
    72.     jobs = [gevent.spawn(download_requests, url) for url in urls]  
    73.     gevent.joinall(jobs)  
    74.     #for i in jobs:  
    75.     #    print(i.value[0], i.value[1][:10])  
    76.     #tic(requestsT, 'requests with gevent.spawn')  
    77.     reqSpawn.append(ticT(requestsT))  
    78.     print('4')  
    79.       
    80. import matplotlib.pyplot as plt  
    81. x = list(range(1, N+1))  
    82. plt.plot(x, urllibL, label='urllib')  
    83. plt.plot(x, requestsL, label='requests')  
    84. plt.plot(x, reqPool, label='requests geventPool')  
    85. plt.plot(x, reqSpawn, label='requests Spawn')  
    86. plt.xlabel('test number')  
    87. plt.ylabel('time(s)')  
    88. plt.legend()  
    89. plt.show()  

    运行结果如下:

            从上图可以看到,对于I/O密集型任务,gevent还是能对性能做很大提升的,由于协程的创建、调度开销都比线程小的多,所以可以看到不论使用gevent的Spawn模式还是Pool模式,性能差距不大。

            因为在gevent中需要使用monkey补丁,会提高gevent的性能,但会影响multiprocessing的运行,如果要同时使用,需要如下代码:

    [python] view plain copy
     
    1. gevent.monkey.patch_all(thread=False, socket=False, select=False)  

            可是这样就不能充分发挥gevent的优势,所以不能把multiprocessing Pool、threading Pool、gevent Pool在一个程序中对比。不过比较两图可以得出结论,线程池和gevent的性能最优的,其次是进程池。附带得出个结论,requests库比urllib.request库性能要好一些哈:-)        

    转载请注明:转自http://blog.csdn.net/littlethunder/article/details/40983031

  • 相关阅读:
    又见Dooyoul
    用premake编译跨平台opencv程序
    [try it] 使用Apache Ant
    用OllyDbg做破解
    什么是SEO
    SEO最新百度排名算法调整
    SEO网站外链的建设
    SEO网站title优化
    网站title优化应注意的细节
    [ios]设置ARC 【转】
  • 原文地址:https://www.cnblogs.com/timssd/p/8068538.html
Copyright © 2020-2023  润新知