Python 内存占用也太大了。

2018-09-25 10:12:15 +08:00
 skinny

我现有 5 个 collections.Counter 的 pickle 文件,单个文件在 84MB-240MB 之间,总共 664MB,总记录在 3 千多万,实际数据大小也就 400MB 左右。之所以分为 5 个文件是因为之前有几个 GB 的数据,我电脑内存小( 6GB ),又是机械硬盘,根本没办法一下子读取和处理,我分割成小块处理,最后变成了这 5 个文件,然后我想合并这些文件进行最后处理。

最终合并前,我预估过用 C 实现类似的字典( D[char[10],uint])合并,内存用的不会很多,即便是最粗糙的字典实现也只需要 680M 左右内存,我看 pickle 文件加起来才 664MB,就算翻 4 翻的内存占用机器也撑得住,可是一运行内存就被耗光,然后机器死机了,只能强制关机,根据死机前的 Python 占用情况,最终可能需要内存要 6GB-8GB 才能加载处理总共 664M 的这 5 个 pickle 文件。(只有 load 和 plus 操作)

可能有人会问我为什么不用 Redis 或者数据库查询,因为我没安装,我也就这一次需要以这种方式处理这种文件。前面用 Python 处理单个小文件时还好,虽然速度不敢恭维,不过还能接受,胜在写起来简单方便。

目前对 Python 感受就是慢、吃内存,但是写起来简单(当然也有非常复杂的,比如 asynio ),真的是胶水一样的语言。

22650 次点击
所在节点    Python
98 条回复
hiths
2018-09-25 18:16:40 +08:00
楼主战斗力好强啊,顺便快来看一下楼上的答案吧。
skinny
2018-09-25 19:23:53 +08:00
@wutiantong

你也可以完全不用 pickle 来测试,测试数据你可以简单先生成 3000 万条长度为 3-32 的 ascii printable 字符串(我这最长的一条有 95 个字节,不过非常非常少,多是 6 到 12 个字节的),重复数据条数占 24%,重复数据你随机插入,生成的字符串作为 K,V 你可以随机产生 1 到 2000 万之间的值,,生成测试数据要花时间,你也可以用更好的办法。

说实在的,我一开始就是吐槽 Python 对象内存占用大。

为了避免有人硬说内存泄露,你可以用如下代码(结果一样,内存需要非常多):

```python

# Version: Python 3.6.6 x64

import collections
import glob

counter = collections.Counter()
# 你也可以直接用 dict
# counter = dict()

for path in glob.glob(r'F:\TestData\*.txt'):
with open(path, encoding='UTF-8') as input_stream:
for line in input_stream:
tmp = line.rstrip()
k, v = tmp.rsplit('\t', maxsplit=1)
c = int(v)
del v
del tmp

counter[k] += c

# n = counter.get(k, 0)
# counter[k] = c + n
```

我用.net core 按相似的方法实现了一遍,速度很快,内存占用 3GB:

```c#
// .net core 2.1
//
// Program.cs build options: Release, x64

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace db_conv_cs
{
class Program
{
static void Main(string[] args)
{
Dictionary<string, uint> counter = new Dictionary<string, uint>();

string[] files = new string[]
{
@"F:\TestData\dat-1.txt",
@"F:\TestData\dat-2.txt",
@"F:\TestData\dat-3.txt",
@"F:\TestData\dat-4.txt",
@"F:\TestData\dat-5.txt"
};

foreach (string path in files)
{
InsertItems(path, counter);
}

Console.WriteLine("{0} items", counter.Count);
}

static void InsertItems(string path, Dictionary<string, uint> counter)
{
FileStream file = new FileStream(path, FileMode.Open, FileAccess.Read);
StreamReader reader = new StreamReader(file, Encoding.UTF8);
string line = null;

while ((line = reader.ReadLine()) != null)
{
line = line.TrimEnd('\r', '\n');

int i = line.LastIndexOf('\t');

if (i == 0)
{
continue;
}

string k = line.Substring(0, i);
string v = line.Substring(i + 1);
uint n = uint.Parse(v);

uint c = 0;
counter.TryGetValue(k, out c);
counter[k] = c + n;
}

reader.Close();
file.Close();
}
}
}
```
skinny
2018-09-25 19:29:43 +08:00
自己立一个靶子来攻击,然后我反驳就说我是杠精,也没谁了,你们爱怎么看怎么看吧。

最后:
https://stackoverflow.com/a/30316760
hotsymbol
2018-09-26 00:17:34 +08:00
Golang 了解一下?
lolizeppelin
2018-09-26 00:22:47 +08:00
python int 最少 28 字节 能不大么……
slixurd
2018-09-26 00:35:35 +08:00
本来 Python 的内存模型就设计成这样
内存占用大的飞起,比 Java 还大的多
这有什么好争的,看下对象的格式不就知道了....
zwh2698
2018-09-26 05:05:30 +08:00
在 @dychenyi 兄弟基础上给你补一句,windows 上叫 creatfilemapping,只要你愿意,TB 级别的都可以。
petelin
2018-09-26 07:24:49 +08:00
不是有人硬要说什么什么,是你的杆精。你说没泄露你程序就没泄露了?
人家给出来自己测试数据了,160MB 到内存里 4-5G,多清晰。你不能找着跑一遍看有没有问题?还逼逼了一堆不知道啥玩意,另外你的代码是真的丑,你引入一堆中间变量然后 del 干什么,链式调用不更优雅一点?
skinny
2018-09-26 07:42:00 +08:00
@slixurd 他们一些人是在捏造论点并攻击,没有一个跟我吐槽相关(推荐 hdf5 之类优秀库的除外),你反驳就是杠精、沟通能力、理解能力差,你说到底谁杠精、理解能力差?如果我一开始不说使用场景,他们要喷我什么也不说就瞎说,我说了又扣了个字眼说我不会用,回他们换成别的也可以,然后又说不给真实测试数据和代码,让他们随便塞按描述生成的那么 3000 万条到内存试试就成,range 总会用吧,我都不强求你怎么样了,然后又说我表达能力差。话说我跟他们兜什么圈子,内存占用、对象大小不是明摆着的么!跟杠精争什么!我本来就是吐个槽而已。
skinny
2018-09-26 07:45:02 +08:00
@petelin 用 del 是免得和你这类杠精一样硬说有内存泄露。你要是还觉得有内存泄露你自己去写吧。那段代码怎么简单怎么来,免得优雅了你们又要说内存泄露。
kljsandjb
2018-09-26 08:07:15 +08:00
你需要跟 c 混编
jimisun
2018-09-26 08:13:04 +08:00
tomcat 路过
xrui
2018-09-26 08:17:13 +08:00
同感,之前整了个爬虫,想记录一下已访问过的网址。list.append (网址字符串),我看着任务管理器,每个网址大概消耗 1M 多内存。后来放弃了,写文件了
petelin
2018-09-26 09:15:20 +08:00
@skinny 人家写了啊,逻辑清晰,结果挺好的,说明够用。基本能证明就是你写的不行。
skinny
2018-09-26 09:33:25 +08:00
@petelin 你们是不是扛得精神分裂,最后开始扛自己了。我标题就说得是 Python 太占内存,主贴里也是吐槽的这个,甚至描述都可以让你们生成数据测试。你们开始硬是说什么我不会用 pickle,然后又硬说 pickle 内存泄露,那人写了个测试说 pickle 没有泄露,然后我进一步遂了你们的心,压根儿不用 pickle,再次强调了数据生成和给了新的测试代码,你们照这个试试再杠?
est
2018-09-26 10:41:45 +08:00
@skinny 扛 杠

哈哈哈
lihongjie0209
2018-09-26 10:50:39 +08:00
@skinny 冒泡排序拿 C 写和拿 C#写差不了太多, 时间复杂度放在那里呢. 你要是指望拿汇编写出 O(nlgn)的冒泡排序那是你的问题. Python 对象占用空间当然比 C 大, 但是你代码设计合理吗, 你的代码完美到需要升级语言才能优化的地步? 我怀疑是你代码问题, 和 Python 没什么关系, 你又不提供代码, 又不提供测试数据, 只知道吐槽, 对你没有帮助, 对社区没有帮助.
lihongjie0209
2018-09-26 10:52:04 +08:00
@est 这个帖子很有意思
lihongjie0209
2018-09-26 10:56:53 +08:00
@xrui 你这是在开玩笑, 给代码, 给测试用例, 给 profile 然后再说一个网址可以占用 1M 内存
est
2018-09-26 11:46:57 +08:00
@lihongjie0209 大力出奇迹。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/492253

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX