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
Virace
V2EX  ›  Python

关于读写文件执行速度

  •  
  •   Virace · 2021-04-14 21:01:56 +08:00 · 1800 次点击
    这是一个创建于 1310 天前的主题,其中的信息可能已经有所发展或是发生改变。
    代码不好贴, 因为涉及到很多个文件

    简单就是:
    读取 A 类 文件, 根据偏移量提取出 B 类文件, 解析 B 类文件中的数据.

    A 类文件中有可能有多个 B 类文件.
    A 类文件几十兆, B 类文件几兆.
    B 类文件是不写入本地的, 直接解析.



    大概有接近 1000 个 A 类文件, 随着循环时间越来越长, 解析 B 类文件数据的这个函数执行会越来越慢.
    多数使用过的变量 都有手动用 del 删除


    每次循环 A 文件前 关闭 gc, A 文件使用后开启 gc 手动调用 gc.collect()

    随着循环次数变多, 解析 B 类文件的函数, 就会从最开始的几十毫秒到后来的几秒甚至时间更长.

    占用内存波动不大, 一直是 300M 左右
    第 1 条附言  ·  2021-04-14 22:59:40 +08:00

    用pycharm的profiler工具分析了一下, 时间用在GC和一个函数中.

        def obj_find_one(obj, key, value):
            """
            查询对象数组中key属性等于value的对象
            :param obj: 对象数组
            :param key: 对象属性名
            :param value: 要查找的属性值
            :return:
            """
            for item in obj:
                if getattr(item, key) == value:
                    return item
    

    这个函数是 解析用 模块中的函数

    26 条回复    2021-04-20 10:19:18 +08:00
    imn1
        1
    imn1  
       2021-04-14 21:14:17 +08:00
    那就加大内存使用量,A 全部读入内存后关闭
    Virace
        2
    Virace  
    OP
       2021-04-14 21:19:46 +08:00
    @imn1 还是内存的事么, 测试机内存倒是够用, 实际是要在一个 运行内存只有 1G 的机器上运行. = =
    liprais
        3
    liprais  
       2021-04-14 21:26:04 +08:00 via iPhone
    解析完了记下来不就完了
    abersheeran
        4
    abersheeran  
       2021-04-14 21:29:06 +08:00
    根据偏移量直接读文件某一部分这事可以先上 mmap 优化一下看看。另外你这个确定是慢在解析上吗?是不是内存超限了导致系统自动分配虚拟内存给你,疯狂交换虚拟内存页导致的。我以前遇到过类似问题。你可以先排查一下。
    westoy
        5
    westoy  
       2021-04-14 21:30:22 +08:00
    不要管程序吃多少内存

    跑的时候观察下吃 swap 没有

    你不停做文件读写, 等于不停触发 linux 的文件缓存机制,linux 也不是彻底没内存才会触发 swap

    swap 占用不停变化的话, 直接禁用或者调整内核的 vm.swapiness
    Virace
        6
    Virace  
    OP
       2021-04-14 21:38:44 +08:00
    @abersheeran 对只是慢在解析上, 简单的装饰器计算运行时间的. 测试是在 win 环境下出现的.
    clino
        7
    clino  
       2021-04-14 21:39:22 +08:00
    "随着循环次数变多, 解析 B 类文件的函数, 就会从最开始的几十毫秒到后来的几秒甚至时间更长."
    是解析本身的耗时越来越长吗?不太理解这是什么导致的。
    Virace
        8
    Virace  
    OP
       2021-04-14 21:39:55 +08:00
    @westoy 测试是在 win 环境下出现的. 也会有类似的情况嘛.
    abersheeran
        9
    abersheeran  
       2021-04-14 21:42:34 +08:00
    @Virace Windows 也有虚拟内存的。先排查这个情况再考虑其他的。任何语言都不会因为整个程序的内存占用变多而运算变慢的。出现这情况大概率是虚拟内存的锅。
    geelaw
        10
    geelaw  
       2021-04-14 21:44:30 +08:00 via iPhone
    为什么你要关掉 GC ?
    Virace
        11
    Virace  
    OP
       2021-04-14 21:52:50 +08:00
    @geelaw 经过测试在读入文件后 关闭 GC 执行后续操作 操作完毕在打开 GC 手动清理, 比平常要快. 意思就是如果不关闭的话, 这种越运行越慢的问题会更严重
    crclz
        12
    crclz  
       2021-04-14 22:06:44 +08:00
    建议楼主把代码贴出来,给大家一个最小的可重现的代码,既方便大家调试,也方便排除其他问题。
    还有就是楼主一些地方的表述不太清晰,可能会让项目成员以外的路人产生困惑。例如“随着循环时间越来越长, 解析 B 类文件数据的这个函数执行会越来越慢”
    crclz
        13
    crclz  
       2021-04-14 22:07:53 +08:00
    还有一个建议:用某种 profiler 看看到底哪些函数是罪魁祸首,方便定位问题
    Virace
        14
    Virace  
    OP
       2021-04-14 22:18:19 +08:00
    代码不好贴, 解析的意思, B 类文件是个独有个格式, 经过几个循环能完全读完. 将读完的数据保存到本地. 这是这个解析函数要做的. 确实用 pycharm 的 profiler 运行了一下, 显示 76%的时间都在这个解析函数上.
    keakon
        15
    keakon  
       2021-04-14 22:32:50 +08:00
    如果解析的文件是 json 的话,市面上所有的 json 库都会内存泄露
    Virace
        16
    Virace  
    OP
       2021-04-14 22:35:58 +08:00
    @keakon 解析倒不是, 解析完毕保存的数据是 json.
    clino
        17
    clino  
       2021-04-14 22:44:44 +08:00
    你说解析函数有误导性,看起来其实这个函数不光做了解析还干了很多其他的事情,这里面有很多细节和可能性。
    我的建议是仔细研究这个函数,看到底越来越慢是这个函数的哪一步导致的,如果能找到,那大概率能猜出是什么导致越来越慢。
    xiaoming1992
        18
    xiaoming1992  
       2021-04-14 22:54:18 +08:00
    笨办法,把那个解析函数按照二分法,移除掉一部分功能后再运行试试
    Virace
        19
    Virace  
    OP
       2021-04-14 23:00:41 +08:00
    @clino 用 profiler 工具找出了一个运行比较长的函数, 已经贴到附言上了
    wuwukai007
        20
    wuwukai007  
       2021-04-14 23:42:51 +08:00
    要不用 pysnooper 逐行分析一下?
    gBurnX
        21
    gBurnX  
       2021-04-15 00:24:00 +08:00
    watch -n 1 'free -h'
    htop -d 1
    dstat -t -n -d -c -g -i -l -m -p -s -y --ipc --lock --raw --tcp --udp --unix
    iostat -x -m -d 1

    注意一下函数执行速度,与 内存使用率 / 磁盘活动时间的关系。
    clino
        22
    clino  
       2021-04-15 11:48:49 +08:00
    从 obj_find_one 的实现来看,应该是这个遍历耗时很长,看能否调整数据结构,让这个查询不需要遍历,或者看能否做预处理形成一个中间数据结构让查询不需要遍历。

    gc.collect 的优化看看能不能不要每次都做,还有 gc.collect 的参数你看能不能给 0 试试 gc 效果如何。
    Virace
        23
    Virace  
    OP
       2021-04-15 17:53:37 +08:00
    @clino 去掉这个函数了, 改用字典了, 时间缩短不少. 但开始运行和运行一段时间后性能还是有差别. 目前从 profiler 工具给的信息看, 大部分时间除了几个循环就是 GC 了.
    clino
        24
    clino  
       2021-04-16 09:55:09 +08:00
    gc.collect 的参数调整没用吗?
    另外不做 gc 的后果是什么?内存暴涨?能否重用 dict 之类的对象来减少新对象的创建?还有就是检测一下内存用量,超过临界值了再执行 gc 。
    Virace
        25
    Virace  
    OP
       2021-04-19 22:20:37 +08:00
    @clino 同等情况下手动执行 gc 和自动 gc 占用内存是有区别的, 比如 100 个 A 类文件循环处理时不调用 GC 可能最高内存会超过 1G, 手动调用最高也只在 400M 左右. 但是不管手动调用还是自动, 这个 gc 在整个程序执行时间时间里是得占用个 30 左右. 好多情况得需要额外复制对象在进行处理, 因为原对象后续还需要使用.
    clino
        26
    clino  
       2021-04-20 10:19:18 +08:00
    或者你实在优化有问题,考虑把对象内的数据外包给 redis 处理,这样 python 这里只要做数据查询就可以了,不用面临这种 python 内部内存暴涨的情况
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5084 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 03:52 · PVG 11:52 · LAX 19:52 · JFK 22:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.