python 多进程分析

一,背景

最近工作中需要使用python脚本将一个xlsx文件中的数据全部导入到es中,由于文件中的内容丰富,数量又多。直接通过python文件读入之后在写入es中,时间比较长,总共接近两万条数据从执行程序,到最终入到es完成,差不多要花10分钟。

通过使用多进程的方式,极大地提高了数据导入es的性能。在这里记录一下对python多进程程序进行的一些分析,研究。

二,性能消耗在哪些地方

为了优化导入速度需要找到花费时间较长的地方在哪。一般来说,一个任务执行时间较长会在两个地方。
1.进行复杂的计算。
2.执行IO操作中存在阻塞,长时间等待IO。

三,程序功能说明

该程序有几个部分,首先是使用openpyxl读xlsx文件中的数据,然后放到一个list中。其次,构造es的数据格式,导入数据到es中。由此可以看出来,在这个程序中既包含了复杂计算比如向list中插入数据,又包含IO操作,所以,在这两个部分都存在耗费时间的问题,这就需要具体问题具体分析。


四,实验环境

测试环境一:
系统:单核,单CPU
数据量:2000
操作:读取xlsx,并发送到es中

首先在单核单CPU,单进程情况下:

main 361 ...Sat Nov 11 04:32:49 2017
main 363...Sat Nov 11 04:33:07 2017
---------------
耗时为18s

其次在单核单CPU,多进程情况下:

main 361 ...Sat Nov 11 04:34:47 2017
main 363...Sat Nov 11 04:35:08 2017
---------------
耗时为19s,反而耗时高了

测试环境二:
系统:2核,2CPU
数据量:2000条

2核2CPU的,单进程:

main 361 ...Sat Nov 11 04:40:15 2017
main 363...Sat Nov 11 04:40:33 2017
---------------
耗时18s

2核2CPU,4个进程:

main 361 ...Sat Nov 11 04:41:46 2017
main 363...Sat Nov 11 04:41:59 2017
---------------
耗时为13s

测试环境三:
系统:2核,2CPU
数据量:4000条

2核2CPU的,单进程:

main 361 ...Sat Nov 11 05:30:22 2017
main 363...Sat Nov 11 05:30:52 2017
---------------
耗时30s

2核2CPU,4个进程:

main 361 ...Sat Nov 11 05:29:08 2017
main 363...Sat Nov 11 05:29:28 2017
---------------
耗时20s

五,总结分析

id核心数据量进程耗时
1单核2000单进程18s
2单核20004进程19s
34核2000单进程18s
44核20004进程13s
54核4000单进程30s
64核40004进程20s

1和2对比

结论:
1. 多进程并没有提高程序的性能,反而造成了一定的性能下降。
2. 有些情况多进程并不能提高程序的性能

产生原因:
1. 由于系统为单核系统,所以每次程序运行的时候只有一个进程在使用CPU。
2. 由于该程序并没有长时间IO阻塞的情况,只存在复杂的计算。所以时间都耗费在CPU进行复杂计算上。
3. 由于多进程存在进程间的切换,会消耗一定的时间,所以导致耗时反而比单进程时间还长。


1和3对比

结论
1. 多核对于单进程来说没有任何性能上的提升

产生的原因:
1. 多核单进程,实际上每次只有一个进程在使用CPU。


2和4对比

结论:
1. 多核可以明显提升程序的性能。

产生原因:
1. 每个cpu可以并行的执行程序,不像单核CPU那样是交替并发的执行。


5和6对比

结论
1. 数据量越大多核性能优势体现的越明显。

产生的原因:
1. 理想情况应该是多一个核,性能提升翻一倍,但是由于存在一些进程间的切换,所以没法达到理想情况。数据量越大消耗的时间越多,性能翻倍之后,消耗时间也会翻倍的减少,所以数量越大效果越明显。


六,附上程序源码

#coding=utf-8  
from datetime import datetime  
import threading
from elasticsearch import Elasticsearch  
from elasticsearch import helpers  
from openpyxl import load_workbook
import sys
import time
from multiprocessing import Process
reload(sys)
sys.setdefaultencoding('utf-8') 

def insertApplication(content):
    es = Elasticsearch(hosts=["xx.xx.xx.xx:9200"], timeout=5000)  
    actions = []  

    i=1  
    index = 0
    #print "start generate index..."
    for line in content:
        try:
            action={  
                "_index":"my_test_v1",  
                "_type":"office",  
                "_id":i,  
                "_source":{  
                        u"id":unicode(line[0]),  
                        u"proto":unicode(line[1]).decode('utf8'),  
                        u"sip":unicode(line[2]).decode('utf8'),  
                        u"sport":unicode(line[3]).decode('utf8'),  
                        u"dip":unicode(line[4]).decode('utf8'),  
                        u"dport":unicode(line[5]).decode('utf8'),  
                        u"status":unicode(line[6]),    
                    }  
                }  
        except:
            print "======="
            print line
            print "+++++++"
        i+=1  
        actions.append(action) 
        if(len(actions)==500):
            success, _ = helpers.bulk(es, actions, raise_on_error=True)
            actions = []
            #del actions[0:len(actions)]   
    if (len(actions) > 0):  
        helpers.bulk(es, actions, raise_on_error=True)

def readFile():
    wb = load_workbook(filename=r'~/myfile.xlsx')
    sheets = wb.get_sheet_names()   # 获取所有表格(worksheet)的名字
    sheet0 = sheets[0]  # 第一个表格的名称
    ws = wb.get_sheet_by_name(sheet0) # 获取特定的 worksheet

    # 获取表格所有行和列,两者都是可迭代的
    rows = ws.rows
    columns = ws.columns

    return rows

def sendToEs(rows, start, end):
    content = []
    for row in rows[start:end]:
        line = [col.value for col in row]
        content.append(line)
    insertApplication(content)

def task():
    rows = readFile()
    process_list = []
    count = 1000
    for i in range(0,4):
        process_list.append(Process(target=sendToEs, args=(rows,i*count,(i+1)*count - 1)))

    return process_list

def multiRun():
    process_list = task()
    for process in process_list:
        process.start()

    # 主进程中等待所有子进程退出
    for process in process_list:
        process.join()

def run():
    rows = readFile()
    count = 1000
    for i in range(0,4):
        sendToEs(rows,i*count,(i+1)*count - 1)

if __name__=='__main__':
    print "main 361 ..." + time.ctime()
    #单进程执行
    run()

    #多进程执行
    multiRun()

    print "main 363..." + time.ctime()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值