【问题标题】:map(int, open(fn)) vs map(int, open(fn).readlines()) vs [int(x) for x in open(fn)]map(int, open(fn)) vs map(int, open(fn).readlines()) vs [int(x) for x in open(fn)]
【发布时间】:2022-08-03 10:32:59
【问题描述】:

UPD:解决了几个问题。

我们有四个文件实现,文件中有 10**7 个整数(一个数字 - 一行)。

Case Code. Parameter int=int for non-using global scope
map def without_readlines(int=int):
data = list(map(int, open(\'test.txt\')))
map + readlines def with_readlines(int=int):
​data = list(map(int, open(\'test.txt\').readlines()))
list comprehension def without_readlines_listcomp(int=int):
data = [int(x) for x in open(\'test.txt\')]
list comprehension + readlines def with_readlines_listcomp(int=int):
data = [int(x) for x in open(\'test.txt\').readlines()]

速度测试的第一个问题:

功能测试的代码类似。

from timeit import default_timer
def func():
    pass
if __name__ == \'__main__\':
    st = default_timer()
    func()
    print(default_timer() - st)
without_readlines() with_readlines() without_readlines_listcomp() with_readlines_listcomp()
1.51-1.56 sec 1.6-1.8 sec 1.79-1.82 sec 1.89-1.93 sec

1)为什么列表比较变体和地图变体之间的差异是 2-3 倍? 0.2-0.3 与 0.07-0.12

内存分析的第二个问题。

功能测试的代码类似。

UPD:此方法不显示 map 函数的深度内存使用情况。

from memory_profiler import profile
@profile
def func():
    pass
if __name__ == \'__main__\':
    func()
Mem usage Increment Occurences Line Contents
without_readlines 19.3 MiB

406.0 MiB
19.3 MiB

386.7 MiB
1

1
@profile
def without_readlines(int=int):
data = list(map(int, open(\'test.txt\')))
with_readlines 19.4 MiB

402.4 MiB
19.4 MiB

383.0 MiB
1

1
@profile
def with_readlines(int=int):
data = list(map(int, open(\'test.txt\').readlines()))
without_readlines
listcomp
19.4 MiB

402.5 MiB
19.4 MiB

-24068.2 MiB
1

10000003
@profile
def without_readlines_listcomp(int=int):
data = list(map(int, open(\'test.txt\')))
with_readlines
listcomp
19.4 MiB

1092.4 MiB
19.4 MiB

-4585.2 MiB
1

10000003
@profile
def with_readlines_listcomp(int=int):
data = list(map(int, open(\'test.txt\').readlines()))

2)为什么 listcomp 变体之间的差异大于 600 MiB?它的内存可以存储 10**7 个字符串吗?

回答:是的,它是 10**7 个字符串的对象大小(列表大小 + 此列表中所有字符串的大小)。

from sys import getsizeof
strs = open(\'test.txt\').readlines()
print(getsizeof(strs) + sum(map(getsizeof, strs)))
# 657 984 050

3)为什么地图变体之间的差异小于 85 MiB? 85 MiB - 包含 10**7 个字符串的列表大小。

回答:差异 86 MiB 是带有字符串的列表对象的大小(file.readlines() 的结果)。不列出+所有字符串。仅列出对象。

from sys import getsizeof
print(getsizeof(open(\'test.txt\').readlines()))
# 89 095 160

测试差异不正确。在下一个答案中计算 map 函数的内存使用情况的正确方法。

4)地图功能如何在低级别工作?为什么列表理解函数的内存差异不相似?

回答:因为装饰器@profile 没有显示深度调用的内存使用情况。

对于正确的内存测试,我使用下一种方法。

from memory_profiler import profile, memory_usage
start_mem = memory_usage(max_usage=True)
res = memory_usage(proc=(func), max_usage=True, include_children=True, retval=True)
print(res[0] - start_mem)

该测试的结果:

with_readlines without_readlines with_readlines_listcomp without_readlines_listcomp
1065-1164 MiB 402-475 MiB 1061-1124 MiB 393-468 MiB

此类数据与使用 python 对象的逻辑相融合。

5)增量的负值是什么意思?

  • map 不返回列表,因此您不会比较相同的事物。并且 \"without listcomp\" 正在使用列表理解
  • 对不起,列表(地图())。查了一下这一刻。

标签: python list-comprehension memory-profiling


【解决方案1】:

首先,readlines() 将一个列表分配到内存中,因此需要一个函数调用才能迭代实际数据;它需要迭代整个文件,然后返回,然后您的代码运行。直接迭代文件不会这样做。这解释了为什么需要更长的时间(尽管不像你说的那样是 3 倍)

其次,map 函数返回一个生成器,所以要么你需要做(int(x) for x...) - 一个生成器表达式。或者将list(map(int, open(...))- 转换为列表进行真正的比较。

最后,您应该使用with 关闭文件句柄

with open("file") as f:
    list(map(int, f)) 

并确保您多次运行分析器,并取平均值......我不确定为什么会出现负数以表示内存使用量。 Occurences 值似乎也与内存使用量增加有关

【讨论】:

  • 我检查了转换为列表的时刻。
  • 因记忆而异的问题。为什么差少?我认为readlines() 从文件中返回所有字符串。
  • 这是正确的。
  • 剖析已经好几次了
  • 文件大小 - 83 MiB。 10**8 个字符串的内存。为什么测试之间的差异只有 4 MiB?
猜你喜欢
  • 1970-01-01
  • 2014-07-29
  • 2011-05-20
  • 2015-11-30
  • 1970-01-01
  • 1970-01-01
  • 2016-04-01
  • 2019-01-01
  • 2021-07-17
相关资源
最近更新 更多