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

Python 内存优化问题

  •  
  •   ChenJHua · 166 天前 · 3383 次点击
    这是一个创建于 166 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我发现 import pymongo 就会占用 6M 内存。 我有 100 个独立运行的脚本,如果他们都需要 import pymongo ,那么就会产生 100*6=600M 内存。 可以实现一个 import 缓存,独立运行的脚本都可以优先复用缓存的包,缓存没有才重新导包。 想问问这个 import 缓存能实现吗

    59 条回复    2023-05-06 04:41:49 +08:00
    maocat
        1
    maocat  
       166 天前 via iPhone
    按需加载 from pymongo import xxx
    yingxiangyu
        2
    yingxiangyu  
       166 天前
    独立运行没办法共享吧,如果是直接用多进程应该是可以共享的
    duke807
        4
    duke807  
       166 天前 via Android
    你试了没有?确定不同进程 import 同一个库,该库的可执行代码的部分没有共用?
    ChenJHua
        5
    ChenJHua  
    OP
       166 天前
    @maocat 尝试了一下,__init__.py 里面就 import 了所有了,从 pymongo 导部分东西也会占用这么大
    ChenJHua
        6
    ChenJHua  
    OP
       166 天前
    @yingxiangyu 这样对我的改动太大了,太多脚本了
    ChenJHua
        7
    ChenJHua  
    OP
       166 天前
    @duke807 不共用的,试过了
    ClericPy
        8
    ClericPy  
       166 天前
    麻烦, 自己倒腾个 ipc 算了, 就几行原生代码, 我是照抄官网的 asyncio 和 struct 自己搭个 Unix domain socket 就完事了, 你这情况都用不着这么复杂, 我那是抗上万并发用的
    ChenJHua
        9
    ChenJHua  
    OP
       166 天前
    @passerby233 感谢你的帮助,这种只能对同一个脚本生效。无法满足我的需求
    ChenJHua
        10
    ChenJHua  
    OP
       166 天前
    @ClericPy 不太明白这和 IPC 有啥关系。我这边尝试初始化了一片共享内存出来,import pymongo 想保存到共享内存里面需要序列化,其他地方要想复用需要从共享内存序列化回来,但是序列化回来后还是会占用 6M 内存
    Varchar
        11
    Varchar  
       166 天前
    可以通过使用 Python 的 importlib 和 sys 模块来实现 import 缓存。可以将已经导入的模块对象存储在一个字典中,每次导入模块时先检查该字典中是否已经存在该模块对象,如果存在则直接使用该对象,否则重新导入并将导入结果存储到字典中。以下是一个简单的实现示例:

    ```python
    import importlib
    import sys

    module_cache = {}

    def import_module(module_name):
    if module_name in module_cache:
    return module_cache[module_name]
    module = importlib.import_module(module_name)
    module_cache[module_name] = module
    return module

    # 使用示例
    pymongo = import_module('pymongo')
    ```

    使用该 import_module 函数代替直接导入 pymongo 模块即可实现 import 缓存。

    以上答案来自于 deflash.ai. 😄
    Varchar
        12
    Varchar  
       166 天前
    我没尝试啊,能不能用,楼主自己试。
    ClericPy
        13
    ClericPy  
       166 天前
    @ChenJHua 就是不直接使用 pymongo 对象, 改远程调用或者跨进程... 那个对象本来就一大堆内置方法, 内存省不了多少的, 做个 HTTP 的接口封装一下也行. 一般情况下脚本都不允许直连数据库, 你们可能要求不严格.

    其实我说的就是走远程过程调用相关的事情, 有点跑题了.

    你一百个脚本走一百个进程, 实际上 python 解释器运行时候 builtins 也占用 20 多 MB 了, 节省那 6MB 实际意义不大, 缓存的包你跨进程没法共享, 你说的序列化什么的实际上又新建了一遍对象. 这些脚本可以考虑从多进程转到多线程里面, 就可以共享连接池了.

    现在看到一大堆脚本就头疼, 刚换的工作掉进脚本地狱里了, 所有东西都面向过程毫无抽象逻辑, 怀念有 azkaban 管理的上一份工作
    ChenJHua
        14
    ChenJHua  
    OP
       166 天前
    @Varchar 谢谢大佬,这个是单脚本适用,多脚本就适用不了了
    ChenJHua
        15
    ChenJHua  
    OP
       166 天前
    @ClericPy 明白你意思了,但是我改不动,走远程过程调用改动太大了。我也头秃,现在要做内存优化,抠到极致了,只能抠这个了,多线程或者多进程对我改动也很大,所以比较头秃
    ClericPy
        16
    ClericPy  
       166 天前
    @ChenJHua 我之前把东西跑 hadoop 上也天天优化内存, 你这优化个 6MB 就很伪需求了... Serverless 一般最小规格都 128MB 了, pymongo 导入就这么大, 用精确导入具体对象的方式呢? 然后手动 gc.collect 什么的. 真抠啊
    akira
        17
    akira  
       166 天前
    你同时跑 100 个进程也要能跑的过来才行吧。。与其在内存这么抠,还不如看下怎么更合理的调度,降低同时跑的数量
    whitewinds
        18
    whitewinds  
       166 天前
    你 100 个独立脚本,运行就要 100 个 python 解释器进程,进程之间内存隔离。或者你单独起一个 pymongo 的服务进程,其他脚本进程用 IPC 调它。
    009694
        19
    009694  
       166 天前 via iPhone   ❤️ 7
    大家提了这么多方法,op 就只一句话:没法改。 既然没法改那优化什么内存啊😂
    omgr
        20
    omgr  
       166 天前   ❤️ 1
    一句话,这么在意内存占用就 RIIR ( x

    - 考虑性价比,内存白菜价了,时间和人力成本更贵,真不够不如加内存
    - 解决需要 100 个独立脚本的问题
    - 使用 memray 找到短板,6M 的 pymongo 真的是大头吗,先上 python3.11 看看
    roundgis
        21
    roundgis  
       166 天前 via Android   ❤️ 1
    用 golang or rust 重寫吧
    linggaoyuan123
        22
    linggaoyuan123  
       166 天前
    ipc 改不了,那就改脚本啊,把一百个脚本缩小到 50 个,不就优化一半了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
    vicalloy
        23
    vicalloy  
       166 天前
    Varchar 的答案完全不可行。Python 本身就会提供 import 缓存,这么做完全是画蛇添足。如果你自己无法分别 AI 给的答案是否正确,建议不要直接发上来。
    对于这个需求比较奇怪的一个点是,为啥会需要这么多个独立的脚本,而且还都需要常驻内存。
    ChenJHua
        24
    ChenJHua  
    OP
       166 天前
    @ClericPy 我只是举个例子,实际很多这些 import 。精确导入就看库实现的咋样了,精确导入我尝试了也是一样的资源占用的
    ChenJHua
        25
    ChenJHua  
    OP
       166 天前
    @akira 好的,谢谢你的建议
    ChenJHua
        26
    ChenJHua  
    OP
       166 天前
    @akira 好的,谢谢你的建议,我这边考虑下调度问题
    ChenJHua
        27
    ChenJHua  
    OP
       166 天前
    @009694 都是打工人,时间又不给,资源又不加,那我只能想耗时小的方法了
    ruanimal
        28
    ruanimal  
       166 天前
    加内存啊,现在内存这么便宜。。。
    featureoverload
        29
    featureoverload  
       166 天前
    100 个“脚本程序”,600MB 内存,优化个 X 。

    有用的软件,直接按 32GB ,64GB ,128GB 考虑。

    没有用的软件,“过早优化是万恶之源”。
    ChenJHua
        30
    ChenJHua  
    OP
       166 天前
    @whitewinds 好的,谢谢大佬建议
    yinmin
        31
    yinmin  
       166 天前 via Android
    这个问题无解,除非合并到 1 个进程,用线程 /协程跑多任务。
    ChenJHua
        32
    ChenJHua  
    OP
       166 天前
    @omgr 目前在对比升级 python3.11 看看,谢谢大佬建议
    ChenJHua
        33
    ChenJHua  
    OP
       166 天前
    @roundgis 领导批准就可以干了,哈哈哈哈
    ChenJHua
        34
    ChenJHua  
    OP
       166 天前
    @linggaoyuan123 也有点道理,我这边尝试一下合并,谢谢大佬建议
    ChenJHua
        35
    ChenJHua  
    OP
       166 天前
    @vicalloy 问就是历史遗留,哈哈哈哈
    ChenJHua
        36
    ChenJHua  
    OP
       166 天前
    @ruanimal tob 的设备都卖了,又不是云,不好加只能优化了
    ChenJHua
        37
    ChenJHua  
    OP
       166 天前
    @featureoverload 我只是举个例子,看看能不能优化独立脚本重复导包的内存占用。不早了,资源都没了
    baobao1270
        38
    baobao1270  
       166 天前
    上微服务
    不是让你真的上微服务,但是用微服务的思想解决这个问题
    比如你可以把程序拆解为两个部分,一个部分是其余代码,另一个部分是调用 pymango 的代码
    然后两个程序通过一些方式进行通信,比如 Unix Domain ,比如 gRPC ,比如 HTTP ,或者简单的读写文件也可以
    你的 pymongo 可以单个进程运行,也可以开多个,比如 5 个,但是不需要 100 个那么多
    你的其余代码进程可以 100 个同时运行
    你可以用消息队列,或者生产者-消费者模型,来管理调度
    featureoverload
        39
    featureoverload  
       166 天前
    @ChenJHua “优化独立脚本重复导包的内存占用” 针对这个问题而言。

    如果脚本是一次性执行的。那么可以在父进程(死循环的那个程序)

    1. 先 import pymongo;
    2. 然后 os.fork
    3. 子进程中:import {脚本}; {脚本}.run()
    4. 脚本运行完退出子进程

    从 LINUX(*nix)来说,父进程和子进程的“读”内存是共享的。而 import 的包显然是用来“读”的。

    所以从原理上,逻辑推导是可以做到共享的。

    但是 python 不是编译的直接在 LINUX 系统上直接运行的二进制可执行程序。
    而是通过 cpython 这个二进制程序解释运行的。
    所以实际能不能共享父进程和子进程的内存,要看具体实现,我没有实验过。

    -------------

    如果是我要解决这个问题的话,我会从上面的思路做试验测试看看会不会“优化独立脚本重复导包的内存占用”。
    wuwukai007
        40
    wuwukai007  
       166 天前
    建议上 celery ,控制 celery 的 worker 数量,worker 内执行脚本
    raptor
        41
    raptor  
       166 天前   ❤️ 1
    说实话,就 600M 这点内存费这事干嘛,直接加内存不就完了么?又不是 600G……
    wangxin13g
        42
    wangxin13g  
       166 天前
    你省的这 600m 内存成本远远不如下一个接手这个项目的人浪费的工时值钱
    CloveAndCurrant
        43
    CloveAndCurrant  
       166 天前
    说实话,如果真心在意这点内存,就换语言,换 golang ,可以优先对部分占内存较大的业务进行切换
    anjiannian
        44
    anjiannian  
       166 天前 via Android
    写个 apiserver 提供 mongo 服务,脚本发请求就好了
    mokiki
        45
    mokiki  
       166 天前
    Linux 内核有压缩去重的功能,自己编译内核试试。
    txy3000
        46
    txy3000  
       166 天前 via Android
    600m 要不考虑升级一下配置?
    fgwmlhdkkkw
        47
    fgwmlhdkkkw  
       166 天前
    @CloveAndCurrant 这跟语言有什么关系?你就是用汇编,不一样的进程也是各自独立的内存啊
    zagfai
        48
    zagfai  
       166 天前
    这个本质是复用问题,内存中可以复用的东西多得去了,全部尽可能复用的话,你 10G 系统跑起来可能都没几百 M ,
    现在是内存不值钱,所以大家都拼命摊开来烧内存,而不是努力复用
    houzhiqiang
        49
    houzhiqiang  
       166 天前
    不应该最多是 cpu 核心数个进程或者乘 2 个进程吗?为什么需要 100 个?
    raymanr
        50
    raymanr  
       166 天前
    直接给领导说优化不了, 要么完蛋, 要么你加内存
    Oilybear
        51
    Oilybear  
       165 天前
    如果是单独一个进程的话 import 的是有缓存机制的,但是本身就是 600 个进程...我想不到什么好的方法?把访问 mongo 的部分抽出来做一个 work ,其他的进程使用 socker 或者什么请求这个 work 调用,然后等完拿结果?
    lambdaq
        52
    lambdaq  
       165 天前
    import 不能实现,你得改造成 master - worker 才行。
    winglight2016
        53
    winglight2016  
       165 天前
    真是诡异的优化思路,正常思路不是应该做 100 个脚本的重构吗?

    为什么看到脚本里都有相同的 import 语句就认为可以重用,目的还是节约 600M 内存?

    想节约内存就不要用 python 这种解释型语言啊?
    CloveAndCurrant
        54
    CloveAndCurrant  
       165 天前
    @fgwmlhdkkkw 这和进程当然关有系,单进程 Python 这种万物皆对象的语言就是比 golang 占用内存多,golang 没有线程锁,也不用开那么多进程。自然用的就是内存少。
    fgwmlhdkkkw
        55
    fgwmlhdkkkw  
       165 天前
    @CloveAndCurrant 杠还是你杠
    CloveAndCurrant
        56
    CloveAndCurrant  
       165 天前
    @fgwmlhdkkkw 没有你会杠,也没有你懂,别人都不知道“不一样的进程也是各自独立的内存”,就你知道😂😂😂😂
    linggaoyuan123
        57
    linggaoyuan123  
       161 天前
    另外我不了解 pymongo 这个咋实现的..是包了一层 mongodb 的库?这个包的这个是静态库所以才会这样?
    linggaoyuan123
        58
    linggaoyuan123  
       161 天前
    如果是这样,那改称动态库不就好了.
    mayli
        59
    mayli  
       142 天前 via Android
    Py 本身的原因不好弄 除非你抽象一个框架出来,然后脚本走 rpc. 这样应该是这种共享内存的最优解
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5141 人在线   最高记录 6067   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 46ms · UTC 07:26 · PVG 15:26 · LAX 00:26 · JFK 03:26
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.