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 · 2023-04-11 19:01:31 +08:00 · 4191 次点击
    这是一个创建于 594 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    59 条回复    2023-05-06 04:41:49 +08:00
    maocat
        1
    maocat  
       2023-04-11 19:06:56 +08:00 via iPhone
    按需加载 from pymongo import xxx
    yingxiangyu
        2
    yingxiangyu  
       2023-04-11 19:16:20 +08:00
    独立运行没办法共享吧,如果是直接用多进程应该是可以共享的
    duke807
        4
    duke807  
       2023-04-11 19:21:35 +08:00 via Android
    你试了没有?确定不同进程 import 同一个库,该库的可执行代码的部分没有共用?
    ChenJHua
        5
    ChenJHua  
    OP
       2023-04-11 19:44:00 +08:00
    @maocat 尝试了一下,__init__.py 里面就 import 了所有了,从 pymongo 导部分东西也会占用这么大
    ChenJHua
        6
    ChenJHua  
    OP
       2023-04-11 19:44:16 +08:00
    @yingxiangyu 这样对我的改动太大了,太多脚本了
    ChenJHua
        7
    ChenJHua  
    OP
       2023-04-11 19:44:32 +08:00
    @duke807 不共用的,试过了
    ClericPy
        8
    ClericPy  
       2023-04-11 19:46:22 +08:00
    麻烦, 自己倒腾个 ipc 算了, 就几行原生代码, 我是照抄官网的 asyncio 和 struct 自己搭个 Unix domain socket 就完事了, 你这情况都用不着这么复杂, 我那是抗上万并发用的
    ChenJHua
        9
    ChenJHua  
    OP
       2023-04-11 19:46:47 +08:00
    @passerby233 感谢你的帮助,这种只能对同一个脚本生效。无法满足我的需求
    ChenJHua
        10
    ChenJHua  
    OP
       2023-04-11 19:50:42 +08:00
    @ClericPy 不太明白这和 IPC 有啥关系。我这边尝试初始化了一片共享内存出来,import pymongo 想保存到共享内存里面需要序列化,其他地方要想复用需要从共享内存序列化回来,但是序列化回来后还是会占用 6M 内存
    Varchar
        11
    Varchar  
       2023-04-11 19:58:45 +08:00
    可以通过使用 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  
       2023-04-11 19:59:16 +08:00
    我没尝试啊,能不能用,楼主自己试。
    ClericPy
        13
    ClericPy  
       2023-04-11 20:08:39 +08:00
    @ChenJHua 就是不直接使用 pymongo 对象, 改远程调用或者跨进程... 那个对象本来就一大堆内置方法, 内存省不了多少的, 做个 HTTP 的接口封装一下也行. 一般情况下脚本都不允许直连数据库, 你们可能要求不严格.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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