Python程序运行时间和进程池的详解

python==2.7

elasticsearch==6.2.0

1:背景介绍,最近有一个需求,从ELK日志系统前一天的日志中提取url,url要求去重,然后呢,我用了cosine相似度和字典树匹配两种方案来去重,比较之下,字典树的效果还是要好很多的。

现在遇到的瓶颈是有点慢,慢了当然就是想多多线程多进程咯,不过好像python的多线程不怎么能提高效率,于是考虑多进程。

 

2:运行时间

    a = 100
    s1 = time.time()
    # 不要去数个数了,小目标,一个亿
    for x in range(100000000):
        if a > x:
            pass
        else:
            pass
    e1 = time.time()
    print(e1-s1)
    # e1-s1 大概为17.6s,ps:cpu:Xeon(R) E5-2650 v4

    s1 = time.time()
    for x in range(100000000):
        pass
    e1 = time.time()
    print(e1 - s1)
    # e1 - s1 大概为7s   

从上面可以看出,当循环数量上去了之后(我在分析日志时,发现确实上去了),一个if差不多会带来1.5倍的性能开销(在我这个例子中)。或者这样来说,在1亿次的循环中,一个if会额外消耗15秒。

综上,程序中最消耗时间的是循环,(双重循环的话就是n* n),其次是判断语句,一些io啊。值得注意的是很多操作都是用循环实现的。例如很多的数据结构中的查找和更新。

使用多进程的原因,程序有些逻辑是相对更加耗时的,比如在一亿次的循环中的操作,每次都需要1.5秒(这个可以用sleep函数模拟),那么串行执行就实在是一個很糟糕的选择了,这时多线程,多进程就是一个很好的选择。在Python中,我选择了多进程。下面是进程池的详细解释,看完之后基本能理解线程池中的调用了。

 

# -*- coding: utf-8 -*-
import multiprocessing
import time
from multiprocessing import Pool


def run(num):
  #fn: 函数参数是数据列表的一个元素
  time.sleep(1)
  print(multiprocessing.current_process().name)

def modify_data(num):
  time.sleep(2)
  return num

def get_data():
  for num in range(20):
    yield num

if __name__ == "__main__":

  print('===我是分割线1:map===')
  pool = Pool(10)
  s1 = time.time()
  # 主进程阻塞,等待进程池完成
  pool.map(run, get_data())
  e1 = time.time()
  pool.close()
  pool.join()
  print("并行执行时间1:", int(e1 - s1))

  print('===我是分割线2:map_async===')
  pool = Pool(10)
  s1 = time.time()
  pool.map_async(run, get_data())
  # 这里计算出的e1和s1是一样的,因为是异步的
  # e1 = time.time()
  pool.close()
  # 不要去执行下一条命令,等pool中的命令执行完
  pool.join()
  # 这里计算出e1和s1就不一样
  e1 = time.time()
  print("并行执行时间2:", int(e1 - s1))

  # 结论:1和2效果相当,耗时相同

  print('===我是分割线3:apply===')
  pool = Pool(10)
  s1 = time.time()
  for x in get_data():
    # 这个东西如果这样处理的话就不是并行的了, 而且在Python3之后也不推荐
    pool.apply(run, (x,))
  e1 = time.time()
  pool.close()
  pool.join()
  print("串行执行时间3:", int(e1 - s1))

  print('===我是分割线4:apply_async===')
  pool = Pool(10)
  s1 = time.time()
  for x in get_data():
    # 传参数注意传tuple,list
    pool.apply_async(run, [x])
  pool.close()
  pool.join()
  print('flag')
  e1 = time.time()
  print("并行执行时间4:", int(e1 - s1))

  # 结论 3 是串行执行,4 是并行执行,1,2,4效率相当
  # conclusion, the 1,2,4 is a parallel execution, while the 3 is a serial execution

  print('===我是分割线5:串行运算===')
  ll = []
  s1 = time.time()
  for x in get_data():
    # 传参数注意传tuple,list
    temp = modify_data(x)
    if temp > 1:
      ll.append(temp)
  print('flag')
  e1 = time.time()
  print("串行执行时间5:", int(e1 - s1))
  s1 = time.time()
  for l in ll:
    print(l)
  e1 = time.time()
  print("获得数据时间:", int(e1 - s1))

  print('注意apply_async的返回值是一个AppResult的类型,你需要使用get()方法才能得到你的数据')
  print('===我是分割线6:apply_async中修改,获取返回值===')
  ll = []
  pool = Pool(10)
  s1 = time.time()
  # 在这个循环里的语句也会影响程序的并发性
  for x in get_data():
    # 传参数注意传tuple,list
    temp = pool.apply_async(modify_data, [x])
    print(temp)
    ll.append(temp)
  pool.close()
  pool.join()
  print('flag')
  e1 = time.time()
  print("并行执行时间6:", int(e1 - s1))
  s1 = time.time()
  for l in ll:
    print(l.get())
  e1 = time.time()
  print("获得数据时间:", int(e1 - s1))

  print('===我是分割线7:apply_async中修改,获取返回值===')
  ll = []
  pool = Pool(10)
  s1 = time.time()
  # 在这个循环里的语句也会影响程序的并发性
  # 如下面的例子,在这里调用了temp(ApplyResult)的get()方法,程序就会串行执行
  for x in get_data():
    # 传参数注意传tuple,list
    temp = pool.apply_async(modify_data, [x])
    print(temp.get())
    ll.append(temp)
  pool.close()
  pool.join()
  print('flag')
  e1 = time.time()
  print("串行执行时间7:", int(e1 - s1))
  s1 = time.time()
  for l in ll:
    print(l.get())
  e1 = time.time()
  print("获得数据时间:", int(e1 - s1))

  # 结合6,7,得出结论,在循环中不能调用get()方法,如果调用,程序将会串行执行,
  # 这应该是Python多进程对使用者的限制,

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值