简短版本:
上下文:我的训练数据输入管道的开始是一个tf.data.Dataset.from_generator
调用。
使用缓冲的暂存区而不是直接通过python生成器流式传输数据时,我遇到了意外的性能下降。缓冲的暂存区域由多个生产者(在C ++中)填充,并通过python生成器使用。尽管缓冲生成器的原始吞吐量更高,但是当以单线程合成数据按需方式通过python生成器流传输数据时,性能会显着提高。我不确定为什么会这样。
上下文和(半)规范:
我们将使用C ++创建的合成图像用作训练的示例数据。数据综合程序是线程安全的,因此可以通过并发生成来提高吞吐量。 Python(CPython)具有令人讨厌的GIL,因此我们在C ++中实现了一个缓冲的暂存区。这个暂存区域由多个生产者填充,并通过我们的python生成器(C ++->使用ctypes
的python过渡)消费。
此程序结构的目的是通过最小化python生成器中完成的工作来提高数据IO的速度。
在测试python生成器的原始吞吐量时,这在简单情况下非常有用。在琐碎的Tensorflow程序的上下文中工作时,它也可以正常运行,该程序仅从此生成器创建一个数据集并迭代N个示例。
但是,当插入我们的实际训练管道时,缓冲生成器的功能似乎不太合适。
生产者线程非常快地将缓冲区填充到Tensorflow训练程序的生命周期中,并且几乎在大多数时间都在等待(不忙于等待)。我的理解是,一旦缓冲区域已满,该管道中的IO就变得微不足道了,因为所有python生成器需要做的就是从其大量的预格式化样本中提取一个样本,然后yield
传递到Tensorflow运行时(通过tf.data.Dataset.from_generator
)。
问题在于,当将此生成器插入我们的 actual Tensorflow训练程序中时,吞吐量显着低于过去使用静态数据或无缓冲版本的数据生成器时的吞吐量(在非玩具TF运行时中使用时,缓冲版本的速度大约是非缓冲版本的40%)。无论生产者线程的数量如何,缓冲的暂存区域的吞吐量都非常糟糕。
我现在对这种行为的来源不知所措,我想知道外面是否有人对可能发生的事情有任何见识。据我所知,生成器函数按其应有的方式运行,有适当数量的线程在适当的时间进行了适当的工作。但是,在事物的Tensorflow方面,我对事物到底是如何工作的不太清楚。
其他信息:
我认为这可能与Tensorflow Dataset API(子标题:from_generator)提供的以下注释有关:
注意:如果生成器依赖于可变的全局变量或其他外部状态,请注意运行时可能会多次调用生成器(以支持重复数据集),并且在两次调用Dataset.from_generator()和生成器生成的第一个元素。突变全局变量或外部状态可能导致未定义的行为,我们建议您在调用Dataset.from_generator()之前在生成器中显式缓存任何外部状态。
与此python生成器相关联的是一些外部状态,看成它是如何从MPSC缓冲区中弹出的(该缓冲区正在单独的线程中填充),但是我不太了解这种状态的原因。造成问题。
我认识到自定义Tensorflow操作是另一种选择,但我也想尽可能地找出此问题的根本原因。
如果需要的话,我可以包含一些源代码,但是我担心的是,底层方法可能存在缺陷。
我的问题是:
此程序设计有天生的问题吗,还是这种行为可能是我的代码中的某些错误导致的?
感谢您的帮助,如果需要更多详细信息,请告诉我。