我发现 import pymongo 就会占用 6M 内存。 我有 100 个独立运行的脚本,如果他们都需要 import pymongo ,那么就会产生 100*6=600M 内存。 可以实现一个 import 缓存,独立运行的脚本都可以优先复用缓存的包,缓存没有才重新导包。 想问问这个 import 缓存能实现吗
1
maocat 2023-04-11 19:06:56 +08:00 via iPhone
按需加载 from pymongo import xxx
|
2
yingxiangyu 2023-04-11 19:16:20 +08:00
独立运行没办法共享吧,如果是直接用多进程应该是可以共享的
|
3
passerby233 2023-04-11 19:19:37 +08:00
|
4
duke807 2023-04-11 19:21:35 +08:00 via Android
你试了没有?确定不同进程 import 同一个库,该库的可执行代码的部分没有共用?
|
5
ChenJHua OP @maocat 尝试了一下,__init__.py 里面就 import 了所有了,从 pymongo 导部分东西也会占用这么大
|
6
ChenJHua OP @yingxiangyu 这样对我的改动太大了,太多脚本了
|
8
ClericPy 2023-04-11 19:46:22 +08:00
麻烦, 自己倒腾个 ipc 算了, 就几行原生代码, 我是照抄官网的 asyncio 和 struct 自己搭个 Unix domain socket 就完事了, 你这情况都用不着这么复杂, 我那是抗上万并发用的
|
9
ChenJHua OP @passerby233 感谢你的帮助,这种只能对同一个脚本生效。无法满足我的需求
|
10
ChenJHua OP @ClericPy 不太明白这和 IPC 有啥关系。我这边尝试初始化了一片共享内存出来,import pymongo 想保存到共享内存里面需要序列化,其他地方要想复用需要从共享内存序列化回来,但是序列化回来后还是会占用 6M 内存
|
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. 😄 |
12
Varchar 2023-04-11 19:59:16 +08:00
我没尝试啊,能不能用,楼主自己试。
|
13
ClericPy 2023-04-11 20:08:39 +08:00
@ChenJHua 就是不直接使用 pymongo 对象, 改远程调用或者跨进程... 那个对象本来就一大堆内置方法, 内存省不了多少的, 做个 HTTP 的接口封装一下也行. 一般情况下脚本都不允许直连数据库, 你们可能要求不严格.
其实我说的就是走远程过程调用相关的事情, 有点跑题了. 你一百个脚本走一百个进程, 实际上 python 解释器运行时候 builtins 也占用 20 多 MB 了, 节省那 6MB 实际意义不大, 缓存的包你跨进程没法共享, 你说的序列化什么的实际上又新建了一遍对象. 这些脚本可以考虑从多进程转到多线程里面, 就可以共享连接池了. 现在看到一大堆脚本就头疼, 刚换的工作掉进脚本地狱里了, 所有东西都面向过程毫无抽象逻辑, 怀念有 azkaban 管理的上一份工作 |
15
ChenJHua OP @ClericPy 明白你意思了,但是我改不动,走远程过程调用改动太大了。我也头秃,现在要做内存优化,抠到极致了,只能抠这个了,多线程或者多进程对我改动也很大,所以比较头秃
|
16
ClericPy 2023-04-11 20:38:43 +08:00
@ChenJHua 我之前把东西跑 hadoop 上也天天优化内存, 你这优化个 6MB 就很伪需求了... Serverless 一般最小规格都 128MB 了, pymongo 导入就这么大, 用精确导入具体对象的方式呢? 然后手动 gc.collect 什么的. 真抠啊
|
17
akira 2023-04-11 21:43:24 +08:00
你同时跑 100 个进程也要能跑的过来才行吧。。与其在内存这么抠,还不如看下怎么更合理的调度,降低同时跑的数量
|
18
whitewinds 2023-04-12 00:21:24 +08:00
你 100 个独立脚本,运行就要 100 个 python 解释器进程,进程之间内存隔离。或者你单独起一个 pymongo 的服务进程,其他脚本进程用 IPC 调它。
|
19
009694 2023-04-12 00:40:07 +08:00 via iPhone 7
大家提了这么多方法,op 就只一句话:没法改。 既然没法改那优化什么内存啊😂
|
20
omgr 2023-04-12 08:03:48 +08:00 1
一句话,这么在意内存占用就 RIIR ( x
- 考虑性价比,内存白菜价了,时间和人力成本更贵,真不够不如加内存 - 解决需要 100 个独立脚本的问题 - 使用 memray 找到短板,6M 的 pymongo 真的是大头吗,先上 python3.11 看看 |
21
roundgis 2023-04-12 09:06:55 +08:00 via Android 1
用 golang or rust 重寫吧
|
22
linggaoyuan123 2023-04-12 09:09:31 +08:00
ipc 改不了,那就改脚本啊,把一百个脚本缩小到 50 个,不就优化一半了。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
|
23
vicalloy 2023-04-12 09:13:44 +08:00
Varchar 的答案完全不可行。Python 本身就会提供 import 缓存,这么做完全是画蛇添足。如果你自己无法分别 AI 给的答案是否正确,建议不要直接发上来。
对于这个需求比较奇怪的一个点是,为啥会需要这么多个独立的脚本,而且还都需要常驻内存。 |
24
ChenJHua OP @ClericPy 我只是举个例子,实际很多这些 import 。精确导入就看库实现的咋样了,精确导入我尝试了也是一样的资源占用的
|
28
ruanimal 2023-04-12 09:42:33 +08:00
加内存啊,现在内存这么便宜。。。
|
29
featureoverload 2023-04-12 09:43:21 +08:00
100 个“脚本程序”,600MB 内存,优化个 X 。
有用的软件,直接按 32GB ,64GB ,128GB 考虑。 没有用的软件,“过早优化是万恶之源”。 |
30
ChenJHua OP @whitewinds 好的,谢谢大佬建议
|
31
yinmin 2023-04-12 09:44:17 +08:00 via Android
这个问题无解,除非合并到 1 个进程,用线程 /协程跑多任务。
|
34
ChenJHua OP @linggaoyuan123 也有点道理,我这边尝试一下合并,谢谢大佬建议
|
37
ChenJHua OP @featureoverload 我只是举个例子,看看能不能优化独立脚本重复导包的内存占用。不早了,资源都没了
|
38
baobao1270 2023-04-12 09:48:59 +08:00
上微服务
不是让你真的上微服务,但是用微服务的思想解决这个问题 比如你可以把程序拆解为两个部分,一个部分是其余代码,另一个部分是调用 pymango 的代码 然后两个程序通过一些方式进行通信,比如 Unix Domain ,比如 gRPC ,比如 HTTP ,或者简单的读写文件也可以 你的 pymongo 可以单个进程运行,也可以开多个,比如 5 个,但是不需要 100 个那么多 你的其余代码进程可以 100 个同时运行 你可以用消息队列,或者生产者-消费者模型,来管理调度 |
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 这个二进制程序解释运行的。 所以实际能不能共享父进程和子进程的内存,要看具体实现,我没有实验过。 ------------- 如果是我要解决这个问题的话,我会从上面的思路做试验测试看看会不会“优化独立脚本重复导包的内存占用”。 |
40
wuwukai007 2023-04-12 10:22:17 +08:00
建议上 celery ,控制 celery 的 worker 数量,worker 内执行脚本
|
41
raptor 2023-04-12 10:26:11 +08:00 1
说实话,就 600M 这点内存费这事干嘛,直接加内存不就完了么?又不是 600G……
|
42
wangxin13g 2023-04-12 10:30:19 +08:00
你省的这 600m 内存成本远远不如下一个接手这个项目的人浪费的工时值钱
|
43
CloveAndCurrant 2023-04-12 10:38:39 +08:00
说实话,如果真心在意这点内存,就换语言,换 golang ,可以优先对部分占内存较大的业务进行切换
|
44
anjiannian 2023-04-12 11:47:22 +08:00 via Android
写个 apiserver 提供 mongo 服务,脚本发请求就好了
|
45
mokiki 2023-04-12 13:03:38 +08:00
Linux 内核有压缩去重的功能,自己编译内核试试。
|
46
txy3000 2023-04-12 13:25:34 +08:00 via Android
600m 要不考虑升级一下配置?
|
47
fgwmlhdkkkw 2023-04-12 13:49:30 +08:00
@CloveAndCurrant 这跟语言有什么关系?你就是用汇编,不一样的进程也是各自独立的内存啊
|
48
zagfai 2023-04-12 14:45:13 +08:00
这个本质是复用问题,内存中可以复用的东西多得去了,全部尽可能复用的话,你 10G 系统跑起来可能都没几百 M ,
现在是内存不值钱,所以大家都拼命摊开来烧内存,而不是努力复用 |
49
houzhiqiang 2023-04-12 14:50:08 +08:00
不应该最多是 cpu 核心数个进程或者乘 2 个进程吗?为什么需要 100 个?
|
50
raymanr 2023-04-12 14:57:50 +08:00
直接给领导说优化不了, 要么完蛋, 要么你加内存
|
51
Oilybear 2023-04-12 16:22:47 +08:00
如果是单独一个进程的话 import 的是有缓存机制的,但是本身就是 600 个进程...我想不到什么好的方法?把访问 mongo 的部分抽出来做一个 work ,其他的进程使用 socker 或者什么请求这个 work 调用,然后等完拿结果?
|
52
lambdaq 2023-04-12 17:24:20 +08:00
import 不能实现,你得改造成 master - worker 才行。
|
53
winglight2016 2023-04-13 08:56:07 +08:00
真是诡异的优化思路,正常思路不是应该做 100 个脚本的重构吗?
为什么看到脚本里都有相同的 import 语句就认为可以重用,目的还是节约 600M 内存? 想节约内存就不要用 python 这种解释型语言啊? |
54
CloveAndCurrant 2023-04-13 10:22:09 +08:00
@fgwmlhdkkkw 这和进程当然关有系,单进程 Python 这种万物皆对象的语言就是比 golang 占用内存多,golang 没有线程锁,也不用开那么多进程。自然用的就是内存少。
|
55
fgwmlhdkkkw 2023-04-13 10:38:01 +08:00
@CloveAndCurrant 杠还是你杠
|
56
CloveAndCurrant 2023-04-13 10:43:24 +08:00
@fgwmlhdkkkw 没有你会杠,也没有你懂,别人都不知道“不一样的进程也是各自独立的内存”,就你知道😂😂😂😂
|
57
linggaoyuan123 2023-04-16 22:11:08 +08:00
另外我不了解 pymongo 这个咋实现的..是包了一层 mongodb 的库?这个包的这个是静态库所以才会这样?
|
58
linggaoyuan123 2023-04-16 22:12:01 +08:00
如果是这样,那改称动态库不就好了.
|
59
mayli 2023-05-06 04:41:49 +08:00 via Android
Py 本身的原因不好弄 除非你抽象一个框架出来,然后脚本走 rpc. 这样应该是这种共享内存的最优解
|