• Python 程序慢的像蜗牛,我该怎么办?


    1. 

    “一猿小讲”的风格就是多元化,偶尔会真情吐露一下程序猿的内心;偶尔也结合自己的经历畅聊一些经验杂谈;其中也不乏幽默风趣的技术故事。分享是件快乐的事情,工作之余,有时间我就尽力多码字,多推几篇文章。其实讲真,我每次都是抱着分享给那些需要的人,说不定哪篇文章,就戳中了你,扣开了你的心扉,解决了你的困惑。

     

    好了,不扯啦,言归正传,不跑偏。请准备好小板凳,我们的分享开始。

     

    2. 

    经常理财投资的都清楚,投资的产品周期大概分为 12 个月、24 个月、36 个月。记得上次在信用风险模型项目实现中,为了跑信用风险模型,按照业务要求,需要按照产品周期的维度进行数据逐条拆分、衍生、细化。

     

    站在技术的视角,其实需要针对每条数据,统一执行一个函数,未曾想 Python 天然提供 apply 函数,当时也没管三七二十一,直接拿来主义上来就是用。但是程序跑起来,由于数据量大的原因,一个进程一条一条去执行数据。等输出结果,真是在线等的好着急,就这样程序跑了一整天,苦苦的等出来了结果,你可能不相信,我居然能忍受这么慢的程序,连我自己都不敢相信。

     

    不过当拿到跑出来的结果,却有点不尽人意,于是业务要求加大数据量。但是我的程序这么慢,如果加大数据量,程序跑起来,如果再死等程序的结果,到最后就只能变成了等死啦。

     

    640?wx_fmt=jpeg

    没法,只有技术可以治愈金融危机的创伤;只有技术才能让业务更美好;IT优化没有终点,极致体验没有尽头。那我只能再考虑如何优化一下代码,提升一下程序性能。再三思索,最后决定采取多进程的方式进行了调整。其实和吃包子是一样式的,想想一个人吃 10 个包子和 5 个人吃 10 个包子,那场面效果能一样吗?不过调整后的程序,运行效率确实大幅提升。

     

    640?wx_fmt=png

    唯恐你们也再纠结此种问题,为了你们不再入坑,省出更多时间冲咖啡。作为一个负责任分享的我,岂能只截一张图给你们,还是从实际项目中简单抽取一个 demo 雏形出来,以备你们的不时之需。

    if __name__ == '__main__':	
        # 把36期的数据按照50000条进行分割成小文件	
        step = split_36_months()	
        # 注意:采用多进程进行执行,不然真的会很慢呦	
        # 一个进程处理一个36期的小 csv 文件,进行按照6个月的维度进行细分	
        p = Pool()	
        for i in range(1, step + 1):	
            p.apply_async(add_months_36_months, args=(i,))	
        print('等待所有36期数据处理的子进程执行完毕...')	
        p.close()	
        p.join()	
        print('所有36期数据处理的子进程执行完成')
    

      

    然后定义 split_36_months 函数,完成大的 csv 文件拆分成小 csv 文件。

    # 把36期的csv文件拆分成若干小文件	
    def split_36_months():	
        # TODO 把csv文件拆分成小文件	
        # TODO 统计拆分的小文件个数,这里假设拆分成为5个小文件	
        return 5
    

      

    接着定义 add_months_36_months 函数,完成数据的业务处理(函数名不重要,函数名能起成这样,也是人才,不过这也不是一时的事情,是历史迭代,所以各位看官,莫纠结,莫纠结,莫纠结)。

    # 36期的数据逐个以6个月的维度进行拆分	
    def add_months_36_months(step):	
        print('step: {0} 进程ID:{1} 开始执行任务'.format(step, os.getpid()))	
        # TODO 针对每条数据执行apply函数	
        # chunk = chunk.apply(add_months, axis=1, periods=(36 + 1))	
        # print("step:{0}-{1}月份处理完成".format(step, count))	
    
    	
        # chunk = chunk.apply(format_reserve_tm, axis=1)	
        # print("step:{0}-{1}开始格式化还款日期".format(step, count))	
        # TODO 把执行结果输出到csv文件中	
        print("step:{0} 进程ID: {1} 任务处理完毕".format(step, os.getpgid()))
    

      

    代码码完了,真金不怕火炼,效果不怕检验,是骡子是马总要牵出来遛一遛。程序运行效果如预期所料,拆分成5个小文件,然后每个文件对应一个进程去完成业务数据处理,着实不错。

     

    640?wx_fmt=png

    640?wx_fmt=jpeg

     

    但是知其然,知其所以然,容我再多絮叨两句。

     

    第一步:创建进程池。Python 中如果要启动大量的子进程,那么就可以用进程池的方式批量创建子进程。

    p = Pool()  #默认进程数量是CPU的核数	
    p = Pool(5) #创建拥有5个进程数量的进程池
    

      

    第二步:执行子进程。

    p.apply_async(add_months_36_months, args=(i,))
    

      

    第三步:告诉主进程,你等着所有子进程运行完毕后在运行剩余部分。

    p.close() #关闭进程池	
    p.join() #等待所有工作进程退出
    

      

    友情提示:对 Pool 对象调用 join() 方法会等待所有子进程执行完毕;调用 join() 之前必须先调用 close(),调用 close() 之后就不能继续添加新的 Process 了。

    3. 

    好了,程序从慢到快的步骤只需要一步,那就是实现思路的转变。今天的分享就到这儿,希望对你有帮助。

  • 相关阅读:
    闭包(closure)与协程共用时要注意的事情
    mysql---视图
    职责链模式
    JavaScript DOM(一)
    9.7 迭代
    [BLE--Link Layer]设备蓝牙地址
    Loopback測试软件AX1用户手冊 V3.1
    操作系统
    OpenCV特征点检測------Surf(特征点篇)
    linux 命令 xxd
  • 原文地址:https://www.cnblogs.com/socoool/p/12629781.html
Copyright © 2020-2023  润新知