生成器是否具有时间效率?
Are generators time efficent?
我知道 python 中的生成器至少是内存有效的,因为它一次处理一个项目,但这如何使其时间有效(如果是的话)?
具体来说,假设我正在使用生成器函数为机器学习任务一次加载一个数据。在一天结束时,我仍然需要遍历所有数据元素并一次加载它们(使用生成器函数)。是的,这是有效的记忆,但是与一次加载所有数据集相比,加载整个数据集应该花费更多的时间。我的直觉是对的吗?
#sample_code
def my_gen():
for i in range(1000):
features = np.random.randn(32,32,3)
labels = np.random.randint(0,1, size = 1)
yield features, labels
没有。生成器本质上比类似的替代方案(如列表理解)慢。
但是
如果您希望通过使用生成器加载数据来减少内存,您可能不应该担心这种性能差异。更常见的情况是性能瓶颈在磁盘 I/O and/or 系统调用处。使用生成器的惩罚对整体性能的影响可以忽略不计。
所以最后的答案是:继续使用发电机。它的性能应该是最后一个需要担心的事情。
生成器是您使用的一项功能。特定任务的实施是另外一回事。
每次可以读取一个项目,也可以每次读取大量数据,每次都进行处理。
因此,根据您的情况,后者可能是更好的选择。在这种情况下,它也会更省时。
将生成器视为惰性序列,通常较少时间效率作为相应的急切序列。
%timeit sum((x*2 for x in range(5000))) # lazy generator
366 µs ± 9.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit sum([x*2 for x in range(5000)]) # eager list
308 µs ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这是因为生成器保持中间状态,必须为每个项目恢复该状态。相比之下,急切地创建一个序列只需要处理一次中间状态。
但是请记住,生成器的开销基本上是固定的。如果每个项目都需要很长时间来计算,那么生成器的恒定开销可以忽略不计。处理项目时 one-at-a-time,s 还允许释放已处理的项目,从而减少流程的总体负载——可能在某个时候达到净时间优势。
生成器的优点是惰性允许表示 无限 序列和 延迟 – 生成器是“n 次 O(i) ”与序列“普通 O(ni)”相比。这允许生成器以可靠的时间效率生成每个项,即使整个过程会无限延迟。
一个无限的、eager 序列将具有无限的时间复杂度,但一个无限的、lazy 生成器只根据需要生成项目。
def randoms():
"""Infinite stream of random numbers"""
while True:
yield random.random()
同样,生成器允许 外部数据源 提供每个项目之间的时间。当数据源在提供项目之间有明显的延迟时,这可以使生成器更高效。
我知道 python 中的生成器至少是内存有效的,因为它一次处理一个项目,但这如何使其时间有效(如果是的话)?
具体来说,假设我正在使用生成器函数为机器学习任务一次加载一个数据。在一天结束时,我仍然需要遍历所有数据元素并一次加载它们(使用生成器函数)。是的,这是有效的记忆,但是与一次加载所有数据集相比,加载整个数据集应该花费更多的时间。我的直觉是对的吗?
#sample_code
def my_gen():
for i in range(1000):
features = np.random.randn(32,32,3)
labels = np.random.randint(0,1, size = 1)
yield features, labels
没有。生成器本质上比类似的替代方案(如列表理解)慢。
但是
如果您希望通过使用生成器加载数据来减少内存,您可能不应该担心这种性能差异。更常见的情况是性能瓶颈在磁盘 I/O and/or 系统调用处。使用生成器的惩罚对整体性能的影响可以忽略不计。
所以最后的答案是:继续使用发电机。它的性能应该是最后一个需要担心的事情。
生成器是您使用的一项功能。特定任务的实施是另外一回事。
每次可以读取一个项目,也可以每次读取大量数据,每次都进行处理。
因此,根据您的情况,后者可能是更好的选择。在这种情况下,它也会更省时。
将生成器视为惰性序列,通常较少时间效率作为相应的急切序列。
%timeit sum((x*2 for x in range(5000))) # lazy generator
366 µs ± 9.24 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
%timeit sum([x*2 for x in range(5000)]) # eager list
308 µs ± 3.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
这是因为生成器保持中间状态,必须为每个项目恢复该状态。相比之下,急切地创建一个序列只需要处理一次中间状态。
但是请记住,生成器的开销基本上是固定的。如果每个项目都需要很长时间来计算,那么生成器的恒定开销可以忽略不计。处理项目时 one-at-a-time,s 还允许释放已处理的项目,从而减少流程的总体负载——可能在某个时候达到净时间优势。
生成器的优点是惰性允许表示 无限 序列和 延迟 – 生成器是“n 次 O(i) ”与序列“普通 O(ni)”相比。这允许生成器以可靠的时间效率生成每个项,即使整个过程会无限延迟。
一个无限的、eager 序列将具有无限的时间复杂度,但一个无限的、lazy 生成器只根据需要生成项目。
def randoms():
"""Infinite stream of random numbers"""
while True:
yield random.random()
同样,生成器允许 外部数据源 提供每个项目之间的时间。当数据源在提供项目之间有明显的延迟时,这可以使生成器更高效。