带有 islice 的生成器循环中可能存在内存泄漏
Likely memory leak in generator loop with islice
我正在处理每个包含几百万条记录的大文件(大约 2GB 解压缩,几百 MB 的 gzip)。
我用 islice
遍历记录,这让我可以得到一小部分(用于调试和开发)或当我想测试代码时得到全部。我注意到我的代码内存使用量大得离谱,因此我试图在我的代码中找到内存泄漏。
下面是 memory_profiler 对配对读取(我打开两个文件并压缩记录)的输出,只有 10**5 个值(默认值被覆盖)。
Line # Mem usage Increment Line Contents
================================================
137 27.488 MiB 0.000 MiB @profile
138 def paired_read(read1, read2, nbrofitems = 10**8):
139 """ Procedure for reading both sequences and stitching them together """
140 27.488 MiB 0.000 MiB seqFreqs = Counter()
141 27.488 MiB 0.000 MiB linker_str = "~"
142 #for rec1, rec2 in izip(read1, read2):
143 3013.402 MiB 2985.914 MiB for rec1, rec2 in islice(izip(read1, read2), nbrofitems):
144 3013.398 MiB -0.004 MiB rec1 = rec1[9:] # Trim the primer variable sequence
145 3013.398 MiB 0.000 MiB rec2 = rec2[:150].reverse_complement() # Trim the low quality half of the 3' read AND take rev complement
146 #aaSeq = Seq.translate(rec1 + rec2)
147
148 global nseqs
149 3013.398 MiB 0.000 MiB nseqs += 1
150
151 3013.402 MiB 0.004 MiB if filter_seq(rec1, direction=5) and filter_seq(rec2, direction=3):
152 3013.395 MiB -0.008 MiB aakey = str(Seq.translate(rec1)) + linker_str + str(Seq.translate(rec2))
153 3013.395 MiB 0.000 MiB seqFreqs.update({ aakey : 1 })
154
155 3013.402 MiB 0.008 MiB print "========================================"
156 3013.402 MiB 0.000 MiB print "# of total sequences: %d" % nseqs
157 3013.402 MiB 0.000 MiB print "# of filtered sequences: %d" % sum(seqFreqs.values())
158 3013.461 MiB 0.059 MiB print "# of repeated occurances: %d" % (sum(seqFreqs.values()) - len(list(seqFreqs)))
159 3013.461 MiB 0.000 MiB print "# of low-score sequences (<20): %d" % lowQSeq
160 3013.461 MiB 0.000 MiB print "# of sequences with stop codon: %d" % starSeqs
161 3013.461 MiB 0.000 MiB print "========================================"
162 3013.504 MiB 0.043 MiB pprint(seqFreqs.most_common(100), width = 240)
简而言之,该代码对记录进行一些过滤,并跟踪字符串在文件中出现的次数(在这种特殊情况下为压缩字符串对)。
100 000 个 150 个字符的字符串,在计数器中具有整数值,应该位于 100 MB 左右的顶部,我使用 @AaronHall 的以下函数检查了这一点。
鉴于 memory_profiler 输出,我怀疑 islice 在迭代过程中不会放弃以前的实体。 google 搜索让我到达了 this bug report,但是它被标记为已解决 Python 2.7,这就是我现在 运行。
有什么意见吗?
编辑: 我试图按照下面的评论跳过 islice
并使用像
这样的 for 循环
for rec in list(next(read1) for _ in xrange(10**5)):
这没有太大区别。是在单个文件的情况下,为了避免izip
也来自itertools
.
我的第二个故障排除想法是检查 gzip.open()
是否读取文件并将其扩展到内存,从而导致此处出现问题。但是 运行 解压缩文件上的脚本没有区别。
请注意,memory_profiler 仅报告每行的最大内存消耗。对于长循环,这可能会产生误导,因为循环的第一行似乎总是报告不成比例的内存量。
这是因为它将循环的第一行与之前行的内存消耗进行比较,这将超出循环。这并不意味着循环的第一行消耗 2985Mb,而是循环内的内存峰值与循环外的内存峰值之间的差异高 2985Mb。
我正在处理每个包含几百万条记录的大文件(大约 2GB 解压缩,几百 MB 的 gzip)。
我用 islice
遍历记录,这让我可以得到一小部分(用于调试和开发)或当我想测试代码时得到全部。我注意到我的代码内存使用量大得离谱,因此我试图在我的代码中找到内存泄漏。
下面是 memory_profiler 对配对读取(我打开两个文件并压缩记录)的输出,只有 10**5 个值(默认值被覆盖)。
Line # Mem usage Increment Line Contents
================================================
137 27.488 MiB 0.000 MiB @profile
138 def paired_read(read1, read2, nbrofitems = 10**8):
139 """ Procedure for reading both sequences and stitching them together """
140 27.488 MiB 0.000 MiB seqFreqs = Counter()
141 27.488 MiB 0.000 MiB linker_str = "~"
142 #for rec1, rec2 in izip(read1, read2):
143 3013.402 MiB 2985.914 MiB for rec1, rec2 in islice(izip(read1, read2), nbrofitems):
144 3013.398 MiB -0.004 MiB rec1 = rec1[9:] # Trim the primer variable sequence
145 3013.398 MiB 0.000 MiB rec2 = rec2[:150].reverse_complement() # Trim the low quality half of the 3' read AND take rev complement
146 #aaSeq = Seq.translate(rec1 + rec2)
147
148 global nseqs
149 3013.398 MiB 0.000 MiB nseqs += 1
150
151 3013.402 MiB 0.004 MiB if filter_seq(rec1, direction=5) and filter_seq(rec2, direction=3):
152 3013.395 MiB -0.008 MiB aakey = str(Seq.translate(rec1)) + linker_str + str(Seq.translate(rec2))
153 3013.395 MiB 0.000 MiB seqFreqs.update({ aakey : 1 })
154
155 3013.402 MiB 0.008 MiB print "========================================"
156 3013.402 MiB 0.000 MiB print "# of total sequences: %d" % nseqs
157 3013.402 MiB 0.000 MiB print "# of filtered sequences: %d" % sum(seqFreqs.values())
158 3013.461 MiB 0.059 MiB print "# of repeated occurances: %d" % (sum(seqFreqs.values()) - len(list(seqFreqs)))
159 3013.461 MiB 0.000 MiB print "# of low-score sequences (<20): %d" % lowQSeq
160 3013.461 MiB 0.000 MiB print "# of sequences with stop codon: %d" % starSeqs
161 3013.461 MiB 0.000 MiB print "========================================"
162 3013.504 MiB 0.043 MiB pprint(seqFreqs.most_common(100), width = 240)
简而言之,该代码对记录进行一些过滤,并跟踪字符串在文件中出现的次数(在这种特殊情况下为压缩字符串对)。
100 000 个 150 个字符的字符串,在计数器中具有整数值,应该位于 100 MB 左右的顶部,我使用 @AaronHall 的以下函数检查了这一点。
鉴于 memory_profiler 输出,我怀疑 islice 在迭代过程中不会放弃以前的实体。 google 搜索让我到达了 this bug report,但是它被标记为已解决 Python 2.7,这就是我现在 运行。
有什么意见吗?
编辑: 我试图按照下面的评论跳过 islice
并使用像
for rec in list(next(read1) for _ in xrange(10**5)):
这没有太大区别。是在单个文件的情况下,为了避免izip
也来自itertools
.
我的第二个故障排除想法是检查 gzip.open()
是否读取文件并将其扩展到内存,从而导致此处出现问题。但是 运行 解压缩文件上的脚本没有区别。
请注意,memory_profiler 仅报告每行的最大内存消耗。对于长循环,这可能会产生误导,因为循环的第一行似乎总是报告不成比例的内存量。
这是因为它将循环的第一行与之前行的内存消耗进行比较,这将超出循环。这并不意味着循环的第一行消耗 2985Mb,而是循环内的内存峰值与循环外的内存峰值之间的差异高 2985Mb。