V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
billgreen1
V2EX  ›  Python

python 程序优化/并行?

  •  
  •   billgreen1 · 2016-03-22 07:23:46 +08:00 · 3950 次点击
    这是一个创建于 3167 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我现在要将股票的 tick 数据转成频率为 1 分钟的数据。 数据是存在 amazon s3, 一个日期(文件夹)下面有 2400+只股票,以 zip 格式存储, zip 里面是 一个 csv 文件。 一个 csv 文件大约在 4000+行。

    可以认为 csv 文件就是有一列是时间,大约间隔 3 秒,从 9:25:xx-15:0x:xx ,一列是价格。

    现在采用的是笨办法,把文件下载到本地,已经下载了一个月的,大约 2G 左右。

    主要写了 2 个函数,一个是读 zip 文件到 pandas 的 dataframe ,另一个是转换 dataframe 至 1min 的频率(这里是使用 dataframe 的 resample 函数)。

    为了使用 resample , dataframe 的 index 需要是 DatetimeIndex 格式。 我尝试了几种方法。

    1. 使用 Pandas 的 DatetimeIndex ,很耗时。
    2. strptime 也是很耗时。
    3. 使用 datetime.datetime , 类似如下调用:
    def parse(time_string):
        year = int(time_string[0:4])
        month = ...
        ...
        dt = datetime.datetime(year,month,...)
        return dt
     
    

    方法 3 比方法 1 、 2 快 3-30 倍左右。但是即使这样,还是很耗时。

    def zip_to_dataframe(zipped_file):
        ...
        # index 之前是字符串格式
        df.index = df.index.map(parse) # 本行耗时占整个函数的 60%
        ...
    

    第二个函数

    def transform_dataframe(df):
        ...
        col_loc = transformed_df.columns.get_loc('open') #取得开盘价所在的列位置
        transformed_df.iloc[0,col_loc] = open_price # 本行耗时占整个函数的 45%
       ...
    

    对于处理一个 zip 文件,两个函数各自耗时 100+毫秒左右。如果按照两个函数耗时 0.25 秒算, 0.25(秒) * 2400(一天有 2400 左右只股票) * 20(一个月大概 20 个左右的交易日) * 12(个月) / 3600(秒) = 40 小时!

    即使我用并行,公司电脑只有双核.也要 20 小时。这只是 1 年的,如果要转换多年的。。。

    我现在想到的:

    1. 优化程序,减少耗时。对我来说很难想的出来了, datetime.datetime 后面应该是用的 C 函数吧?感觉很难提升了。 我想不明白为什么对一个单元格赋值会占用那么大的时间。

    2. 把程序拷到其他电脑,一台电脑跑一年的数据,感觉这办法太土。。。。

    3. 用 spark 分布式?没处理过,公司没用 spark ,我都是单机版玩玩,如果这是正途,我愿意试试。

    4. 请多多赐教

    17 条回复    2016-03-22 14:56:08 +08:00
    casparchen
        1
    casparchen  
       2016-03-22 07:32:34 +08:00 via iPad
    去找找有没有一分钟的数据呢
    loggerhead
        2
    loggerhead  
       2016-03-22 08:23:45 +08:00 via iPhone
    没仔细看,想到两个办法:
    1. 换种语言实现,或者用 pypy
    2. 起多个进程分别对不同的 zip 操作
    clino
        3
    clino  
       2016-03-22 08:39:24 +08:00 via Android
    现在笔记本都有八核了。。。
    elitezhe
        4
    elitezhe  
       2016-03-22 09:08:30 +08:00
    两核不代表只能开两个线程啊,网络通信更多时间应该是花在了等待上,所以还是可以多开几个线程的啊
    bigtan
        5
    bigtan  
       2016-03-22 09:11:19 +08:00
    我天天做这个,颇有心得,速度有办法提高数倍,可以联系我。
    mhycy
        6
    mhycy  
       2016-03-22 09:15:07 +08:00
    方便放个样本和示例么?
    clino
        7
    clino  
       2016-03-22 09:22:50 +08:00
    transformed_df 是什么对象? 它是最终你要的结果吗?
    evilic
        8
    evilic  
       2016-03-22 09:25:22 +08:00
    mark
    lecher
        9
    lecher  
       2016-03-22 09:31:36 +08:00 via Android
    Parse 可以优化,因为是连续时间,没必要每个数据序列都从字符串切割年月日这类的,改成预生成,取起始和结束两个时间段,直接把时间段字典构造好。
    或者改成 singleton 模式,年月日时这类的直接缓存在类变量里面,有缓存的话就不要切割这段字符串了,直接取分秒的切割。
    我倾向于第一种。

    读 zip 文件和解包是磁盘 IO 和 CPU 密集型的活问,用 Python 估计优化提升的空间不大,要加速可能把用 SSD 或者把所有文件都读到内存再处理比较好。
    likuku
        10
    likuku  
       2016-03-22 09:40:39 +08:00
    python 嘛,一个 python 进程也只能用到 1 个 CPU 核,线程也只是一个进程内的。

    我之前这么玩:
    python 使用任务队列 queue ,根据可用的 cpu 核数量,开对应数量的 线程,
    线程去 任务队列领任务,开子进程 subprocess 来处理任务。

    如此这般,可以同时利用多个核。

    虽然丑了点。

    这么作有条件的:
    任务可以互不关联的独立处理,没有依赖性。

    或者,自己在外部解决进程间通讯(简易方法,可以用本地文件 /db/memcache/nosql 提供多进程读写状态)
    gamexg
        11
    gamexg  
       2016-03-22 09:41:30 +08:00
    没细看,记得以前看过一篇说 python date 慢的文章,应该有用

    strptime 真慢……
    https://onebitbug.me/2013/09/27/python-strptime-is-super-slow/
    clino
        12
    clino  
       2016-03-22 10:26:56 +08:00
    @likuku 还可以用 uwsgi 来跑多进程,这样比调子进程更好的是已经加载好了更省时间,比如我前两天刚发现的方方式 /t/265065 是用 rpc 来调用,当然也可以变成 http 之类的接口

    不过楼主最多能利用 2 核 所以可能在考虑分布式,不过分布式的问题是不同的机器如何获取到待加工的文件,以及反馈处理过的数据

    用 celery 之类的不知道合不合适
    billgreen1
        13
    billgreen1  
    OP
       2016-03-22 11:12:49 +08:00
    @elitezhe 数据已经被下载到本地了,如果耗时,也应该主要在 IO 上,不在网络通信上
    UnisandK
        14
    UnisandK  
       2016-03-22 11:20:54 +08:00
    https://www.packet.net/bare-metal/


    开台按量的跑完删机器
    pimin
        15
    pimin  
       2016-03-22 11:27:29 +08:00 via Android
    可以丢点数据和 demo 出来给大家练手嘛
    楼主这个问题就实际使用来说,并不是大问题
    一年的数据 20 小时, 10 年也就 200 小时。
    而且是一次性的,处理一次之后以后就没有需求了
    你先找台机器跑起来,过几天就会解决了。
    当然你可能还没把数据都下载完
    如果有好的优化方案你后面处理也不用那么辛苦
    Zzzzzzzzz
        16
    Zzzzzzzzz  
       2016-03-22 12:06:49 +08:00   ❤️ 1
    @likuku pandas 和 Python 的 IO 操作都是释放 GIL, 多线程可以跑在多核上的.
    lebowsk1s
        17
    lebowsk1s  
       2016-03-22 14:56:08 +08:00
    楼主做量化的? anaconda 有个 mkl 发行版,还有个用到 GPU 的发行版,可以一试,还有,你的数据格式不给出来凭空让人看真不好理解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5291 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 09:00 · PVG 17:00 · LAX 01:00 · JFK 04:00
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.