发电机的效率
Efficiency of generators
我正在编写一些软件来创建复杂波形(实际上是声波)作为数组。从一些原始波形(正弦波等)开始,会有将它们组合起来创建更复杂波的函数,还有更多将这些波组合起来的函数,等等。
它可能看起来像:
f(mult(sine(), env(square(), ramp()))
但要复杂得多。
这样做的一种方法是让每个函数成为一个生成器,这样整个函数树每个元素执行一次,每个生成器每次都产生一个值。
数组可能有几百万个元素,函数树很容易达到 10 层。发电机这样做会不会效率低得离谱?
另一种方法是让每个函数都创建一个 return 整个数组。这可能会更有效,但也有缺点(实现更混乱,计算结束前没有可用的结果,可能会使用大量内存)。
他们总是说你不应该尝试猜测 Python 效率,但在这种情况下生成器会花费很长时间吗?
在我看来,生成器非常适合这项任务。
一些信号的时间有限(如包络线或斜坡),但其他一些信号是无限的(如振荡器)。
使用生成器你不必担心这方面的问题,因为 - 就像 zip()
函数一样 - 将振荡器与包络相结合(例如相乘)的函数只会消耗来自振荡器生成器的有限数量的项目,因为至少有一个生成器可以生成有限数量的样本。
然而,使用生成器非常优雅和 pythonic。
回想一下这样的生成器:
def sine(freq):
phase = 0.0
while True:
yield math.sin(phase)
phase += samplerate/freq
只是 class 的语法糖,如下所示:
class sine:
def __init__(self, freq):
self.freq = freq
self.phase = 0.0
def __iter__(self):
return self
def __next__(self):
v = math.sin(self.phase)
self.phase += samplerate/freq
return v
# for this infinite gen we never raise StopIteration()
因此性能开销并不比您可以手工制作的任何其他解决方案(如块处理,通常用于 DSP 算法)。
如果不是生成单个样本,而是生成样本块(例如一次 1024 个样本),也许您可以获得一些效率。
生成器是惰性序列。当您的序列可能很长时,只要您可以分段操作(按元素或在合理大小的块上),它们就非常适合使用。
这往往会减少您的内存使用峰值。只是不要通过将序列的所有元素存储在某处来破坏它。
我正在编写一些软件来创建复杂波形(实际上是声波)作为数组。从一些原始波形(正弦波等)开始,会有将它们组合起来创建更复杂波的函数,还有更多将这些波组合起来的函数,等等。
它可能看起来像:
f(mult(sine(), env(square(), ramp()))
但要复杂得多。
这样做的一种方法是让每个函数成为一个生成器,这样整个函数树每个元素执行一次,每个生成器每次都产生一个值。
数组可能有几百万个元素,函数树很容易达到 10 层。发电机这样做会不会效率低得离谱?
另一种方法是让每个函数都创建一个 return 整个数组。这可能会更有效,但也有缺点(实现更混乱,计算结束前没有可用的结果,可能会使用大量内存)。
他们总是说你不应该尝试猜测 Python 效率,但在这种情况下生成器会花费很长时间吗?
在我看来,生成器非常适合这项任务。
一些信号的时间有限(如包络线或斜坡),但其他一些信号是无限的(如振荡器)。
使用生成器你不必担心这方面的问题,因为 - 就像 zip()
函数一样 - 将振荡器与包络相结合(例如相乘)的函数只会消耗来自振荡器生成器的有限数量的项目,因为至少有一个生成器可以生成有限数量的样本。
然而,使用生成器非常优雅和 pythonic。
回想一下这样的生成器:
def sine(freq):
phase = 0.0
while True:
yield math.sin(phase)
phase += samplerate/freq
只是 class 的语法糖,如下所示:
class sine:
def __init__(self, freq):
self.freq = freq
self.phase = 0.0
def __iter__(self):
return self
def __next__(self):
v = math.sin(self.phase)
self.phase += samplerate/freq
return v
# for this infinite gen we never raise StopIteration()
因此性能开销并不比您可以手工制作的任何其他解决方案(如块处理,通常用于 DSP 算法)。
如果不是生成单个样本,而是生成样本块(例如一次 1024 个样本),也许您可以获得一些效率。
生成器是惰性序列。当您的序列可能很长时,只要您可以分段操作(按元素或在合理大小的块上),它们就非常适合使用。
这往往会减少您的内存使用峰值。只是不要通过将序列的所有元素存储在某处来破坏它。