Python 使用内存分析器分析代码以增加执行时间
我正在编写一个简单的应用程序,它将一个大的文本文件拆分成更小的文件,我已经编写了它的两个版本,一个使用列表,另一个使用生成器。我使用memory_profiler模块分析了这两个版本,它清楚地显示了generators版本更好的内存效率,但奇怪的是,当分析使用Generator的版本时,它增加了执行时间。下面的演示解释了我的意思 使用列表的版本Python 使用内存分析器分析代码以增加执行时间,python,memory-profiling,Python,Memory Profiling,我正在编写一个简单的应用程序,它将一个大的文本文件拆分成更小的文件,我已经编写了它的两个版本,一个使用列表,另一个使用生成器。我使用memory_profiler模块分析了这两个版本,它清楚地显示了generators版本更好的内存效率,但奇怪的是,当分析使用Generator的版本时,它增加了执行时间。下面的演示解释了我的意思 使用列表的版本 from memory_profiler import profile @profile() def main(): file_name =
from memory_profiler import profile
@profile()
def main():
file_name = input("Enter the full path of file you want to split into smaller inputFiles: ")
input_file = open(file_name).readlines()
num_lines_orig = len(input_file)
parts = int(input("Enter the number of parts you want to split in: "))
output_files = [(file_name + str(i)) for i in range(1, parts + 1)]
st = 0
p = int(num_lines_orig / parts)
ed = p
for i in range(parts-1):
with open(output_files[i], "w") as OF:
OF.writelines(input_file[st:ed])
st = ed
ed = st + p
with open(output_files[-1], "w") as OF:
OF.writelines(input_file[st:])
if __name__ == "__main__":
main()
使用探查器运行时
$ time py36 Splitting\ text\ files_BAD_usingLists.py
Enter the full path of file you want to split into smaller inputFiles: /apps/nttech/rbhanot/Downloads/test.txt
Enter the number of parts you want to split in: 3
Filename: Splitting text files_BAD_usingLists.py
Line # Mem usage Increment Line Contents
================================================
6 47.8 MiB 0.0 MiB @profile()
7 def main():
8 47.8 MiB 0.0 MiB file_name = input("Enter the full path of file you want to split into smaller inputFiles: ")
9 107.3 MiB 59.5 MiB input_file = open(file_name).readlines()
10 107.3 MiB 0.0 MiB num_lines_orig = len(input_file)
11 107.3 MiB 0.0 MiB parts = int(input("Enter the number of parts you want to split in: "))
12 107.3 MiB 0.0 MiB output_files = [(file_name + str(i)) for i in range(1, parts + 1)]
13 107.3 MiB 0.0 MiB st = 0
14 107.3 MiB 0.0 MiB p = int(num_lines_orig / parts)
15 107.3 MiB 0.0 MiB ed = p
16 108.1 MiB 0.7 MiB for i in range(parts-1):
17 107.6 MiB -0.5 MiB with open(output_files[i], "w") as OF:
18 108.1 MiB 0.5 MiB OF.writelines(input_file[st:ed])
19 108.1 MiB 0.0 MiB st = ed
20 108.1 MiB 0.0 MiB ed = st + p
21
22 108.1 MiB 0.0 MiB with open(output_files[-1], "w") as OF:
23 108.1 MiB 0.0 MiB OF.writelines(input_file[st:])
real 0m6.115s
user 0m0.764s
sys 0m0.052s
在没有分析器的情况下运行时
$ time py36 Splitting\ text\ files_BAD_usingLists.py
Enter the full path of file you want to split into smaller inputFiles: /apps/nttech/rbhanot/Downloads/test.txt
Enter the number of parts you want to split in: 3
real 0m5.916s
user 0m0.696s
sys 0m0.080s
现在是使用发电机的那个
@profile()
def main():
file_name = input("Enter the full path of file you want to split into smaller inputFiles: ")
input_file = open(file_name)
num_lines_orig = sum(1 for _ in input_file)
input_file.seek(0)
parts = int(input("Enter the number of parts you want to split in: "))
output_files = ((file_name + str(i)) for i in range(1, parts + 1))
st = 0
p = int(num_lines_orig / parts)
ed = p
for i in range(parts-1):
file = next(output_files)
with open(file, "w") as OF:
for _ in range(st, ed):
OF.writelines(input_file.readline())
st = ed
ed = st + p
if num_lines_orig - ed < p:
ed = st + (num_lines_orig - ed) + p
else:
ed = st + p
file = next(output_files)
with open(file, "w") as OF:
for _ in range(st, ed):
OF.writelines(input_file.readline())
if __name__ == "__main__":
main()
那么,为什么分析首先会让我的代码变慢呢?其次,如果at评测影响了执行速度,那么为什么这个效果没有显示在使用列表的代码版本上。我cpu使用line\u profiler评测了代码,这次我得到了答案,generator版本花费更多时间的原因是因为下面的行
19 2 11126.0 5563.0 0.2 with open(file, "w") as OF:
20 379886 200418.0 0.5 3.0 for _ in range(st, ed):
21 379884 2348653.0 6.2 35.1 OF.writelines(input_file.readline())
而对于列表版本来说,它之所以没有减慢速度,是因为
19 2 9419.0 4709.5 0.4 with open(output_files[i], "w") as OF:
20 2 1654165.0 827082.5 65.1 OF.writelines(input_file[st:ed])
对于列表,只需通过切片获取列表的副本就可以编写新文件,这实际上是一条语句。但是,对于generators版本,通过逐行读取输入文件来填充新文件,这使得每行的内存探查器配置文件都增加了cpu时间。任何时候添加探查器(内存或执行)都会增加开销。特定于内存的探查器很可能是在没有考虑执行速度的情况下编写的,而是专注于最准确地计算内存使用量。在使用专门为测量计算速度而设计的低开销探查器时,不能考虑速度。即使使用低开销的探查器,您的程序也永远不会像没有它时运行得那么快,因为您仍在要求处理器执行更多指令。我理解这一点,但问题是为什么它只影响程序的生成器版本,而不影响使用列表的生成器版本。您必须深入研究源代码才能获得准确答案,但是生成器版本可能会导致内存计数更频繁地触发(许多较小的分配,而不是单个较大的分配)。实际上,我有点好奇,您是否可以堆叠探查器并运行cpu时间配置文件,以查看
内存\u探查器的哪个部分。配置文件使用生成器函数需要更多的时间(你会看到它被调用了多少次,平均需要多长时间才能返回)我已经通过将line_profiler的profile decorator堆叠在memory_profiler的profile decorator上实现了这一点。但是无法获得非常描述性的信息,我会再试一次
19 2 11126.0 5563.0 0.2 with open(file, "w") as OF:
20 379886 200418.0 0.5 3.0 for _ in range(st, ed):
21 379884 2348653.0 6.2 35.1 OF.writelines(input_file.readline())
19 2 9419.0 4709.5 0.4 with open(output_files[i], "w") as OF:
20 2 1654165.0 827082.5 65.1 OF.writelines(input_file[st:ed])