摘要
Python作为动态语言,一直被人诟病运行效率低。其实内置很多高阶的函数如map、filter、zip、reduce等,不仅运行速度快而且对内存的占用做了很大的优化。
本文通过计算20万个数的平方,分别比较使用map函数返回map对象,for循环返回迭代器,和for循环返回list,三种方式的内存占用和CPU耗时,发现map和for循环返回生成器效率远远高于for循环返回list。map函数编程是这三种方式最高效的。
安装测试模块
pip3 install memory_profiler
pip3 install psutil
pip3 install line_profiler
测试代码为map.py
:
import time
NUM = 200000
@profile
def gen_for_time():
x = (i ** 2 for i in range(NUM))
@profile
def map_time():
y = map(lambda x: x**2, range(NUM))
@profile
def list_for_time():
y = [i ** 2 for i in range(NUM)]
gen_for_time()
map_time()
list_for_time()
测试CPU耗时
运行系统命令
kernprof -l -v map.py
[vincent@MBP tmp ]$ kernprof -l -v map.py
Wrote profile results to map.py.lprof
Timer unit: 1e-06 s
Total time: 8e-06 s
File: map.py
Function: gen_for_time at line 5
Line # Hits Time Per Hit % Time Line Contents
==============================================================
5 @profile
6 def gen_for_time():
7 1 8.0 8.0 100.0 x = (i ** 2 for i in range(NUM))
Total time: 2e-06 s
File: map.py
Function: map_time at line 9
Line # Hits Time Per Hit % Time Line Contents
==============================================================
9 @profile
10 def map_time():
11 1 2.0 2.0 100.0 y = map(lambda x: x**2, range(NUM))
Total time: 0.079965 s
File: map.py
Function: list_for_time at line 13
Line # Hits Time Per Hit % Time Line Contents
==============================================================
13 @profile
14 def list_for_time():
15 1 79965.0 79965.0 100.0 y = [i ** 2 for i in range(NUM)]
可以看到map用时为 2e-06 s,生成器用时8e-06 s,而普通列表用时0.079965 s,很明显map和迭代器用时远远低于列表。在数据较大(过万)时,列表追加元素的速度会非常慢(后续补充列表实现原理)。下面再来看看内存的占用比较:
python3 -m memory_profiler map.py
Filename: map.py
Line # Mem usage Increment Line Contents
================================================
5 59.730 MiB 59.730 MiB @profile
6 def gen_for_time():
7 59.734 MiB 0.004 MiB x = (i ** 2 for i in range(NUM))
Filename: map.py
Line # Mem usage Increment Line Contents
================================================
9 59.734 MiB 59.734 MiB @profile
10 def map_time():
11 59.734 MiB 0.000 MiB y = map(lambda x: x**2, range(NUM))
Filename: map.py
Line # Mem usage Increment Line Contents
================================================
13 59.734 MiB 59.734 MiB @profile
14 def list_for_time():
15 66.809 MiB -2637.891 MiB y = [i ** 2 for i in range(NUM)]
Increment表示增量,结果发现,map > 迭代器 > 列表。而且列表非常糟糕,每次都显式的存储元素,导致占用内存非常高达到2G。
总结
map函数返回的是map对象,其实map对象也是一种迭代器,因此在内存占用和CPU耗时上,比普通迭代器稍好。这说明map函数做了更多的优化,后续继续探究map的实现原理。
到此我们比较完三种编程方式,for循环配合列表的这种方式,效率非常低,以后的编程过程中尽量少使用。