为什么 return 列表比附加到 return 变量更快?
Why is returning a list faster than appending to a return variable?
在我看来,使用 return 变量而不是使用 return 语句意味着您应该跳过列表的副本。它的可读性不佳,但我认为它会更快。
所以我测试了一下:
import timeit
TEST_DATA = 10000000
ITERATIONS = 1000
time_taken = timeit.timeit(
"""
def get_big_list(test_data):
return [random.random() for _ in range(100000)]
return_list = get_big_list(test_data)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
print("return_list", time_taken / ITERATIONS)
# ----------------------------- SECOND TEST -----------------------------------
time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
[return_list.append(random.random()) for _ in range(100000)]
return_list = []
get_big_list(test_data, return_list)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
print("return_list", time_taken / ITERATIONS)
我对收到的数字感到非常惊讶,我不明白为什么 return 语句更快。
$ python large_list_return.py
('return_list', 0.013130356788635254)
('return_list', 0.022573610067367553)
$ python3 large_list_return.py
return_list 0.016797171516984236
return_list 0.02749005461903289
我发现的另一件有趣的事情是使用 for 循环追加比使用列表理解更快。
time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
for index in range(100000):
return_list.append(random.random())
return_list = []
get_big_list(test_data, return_list)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
return编辑了这些数字
python large_list_return.py
('return_list', 0.0133241708278656)
('return_list', 0.019642770051956176)
python3 large_list_return.py
return_list 0.017075919962022453
return_list 0.024502045304980128
Python 不会制作副本,除非您明确要求副本(例如,通过获取列表的 切片 )——您只是得到另一个引用无论如何创建的列表。
append
另一方面,可能需要获取更多内存并复制以前的内容——它的摊销时间为 O(1),但仍然需要更多工作。
此外,由于您对应该是循环的内容使用了列表理解:
[return_list.append(random.random()) for _ in range(100000)]
在您的第二个实现中,您 还 创建了另一个列表(然后您将其丢弃),一个由 None
的十万次出现组成(来自 append
的 return 值。这就是 for
在那里更快的原因——它避免了无用地创建冗余列表。
如果您正在寻找优化,请考虑 提升 命名空间查找,正如我在上面的评论中提到的那样。具体来说,使用:
def get_big_list(test_data):
r = random.random
return [r() for _ in range(100000)]
在我过时的笔记本电脑上,这需要 12 毫秒,而您的第一个版本是 17 毫秒(这告诉我们每个 random.random
查找大约需要 50 纳秒)。
在我看来,使用 return 变量而不是使用 return 语句意味着您应该跳过列表的副本。它的可读性不佳,但我认为它会更快。
所以我测试了一下:
import timeit
TEST_DATA = 10000000
ITERATIONS = 1000
time_taken = timeit.timeit(
"""
def get_big_list(test_data):
return [random.random() for _ in range(100000)]
return_list = get_big_list(test_data)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
print("return_list", time_taken / ITERATIONS)
# ----------------------------- SECOND TEST -----------------------------------
time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
[return_list.append(random.random()) for _ in range(100000)]
return_list = []
get_big_list(test_data, return_list)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
print("return_list", time_taken / ITERATIONS)
我对收到的数字感到非常惊讶,我不明白为什么 return 语句更快。
$ python large_list_return.py
('return_list', 0.013130356788635254)
('return_list', 0.022573610067367553)
$ python3 large_list_return.py
return_list 0.016797171516984236
return_list 0.02749005461903289
我发现的另一件有趣的事情是使用 for 循环追加比使用列表理解更快。
time_taken = timeit.timeit(
"""
def get_big_list(test_data, return_list):
for index in range(100000):
return_list.append(random.random())
return_list = []
get_big_list(test_data, return_list)
""",
setup='import random; test_data ={0}'.format(TEST_DATA), number=ITERATIONS)
return编辑了这些数字
python large_list_return.py
('return_list', 0.0133241708278656)
('return_list', 0.019642770051956176)
python3 large_list_return.py
return_list 0.017075919962022453
return_list 0.024502045304980128
Python 不会制作副本,除非您明确要求副本(例如,通过获取列表的 切片 )——您只是得到另一个引用无论如何创建的列表。
append
另一方面,可能需要获取更多内存并复制以前的内容——它的摊销时间为 O(1),但仍然需要更多工作。
此外,由于您对应该是循环的内容使用了列表理解:
[return_list.append(random.random()) for _ in range(100000)]
在您的第二个实现中,您 还 创建了另一个列表(然后您将其丢弃),一个由 None
的十万次出现组成(来自 append
的 return 值。这就是 for
在那里更快的原因——它避免了无用地创建冗余列表。
如果您正在寻找优化,请考虑 提升 命名空间查找,正如我在上面的评论中提到的那样。具体来说,使用:
def get_big_list(test_data):
r = random.random
return [r() for _ in range(100000)]
在我过时的笔记本电脑上,这需要 12 毫秒,而您的第一个版本是 17 毫秒(这告诉我们每个 random.random
查找大约需要 50 纳秒)。