首页 > 解决方案 > 数据集 `from_generator` 缓冲暂存区性能不佳

问题描述

简短版本:
上下文:我的训练数据输入管道的开始是一个tf.data.Dataset.from_generator调用。

使用缓冲暂存区域而不是直接通过 python 生成器流式传输数据时,我遇到了意外的糟糕性能。缓冲暂存区由多个生产者(在 C++ 中)填充,并通过 python 生成器使用。尽管缓冲生成器的原始吞吐量更高,但以单线程根据需要合成数据的方式通过 python 生成器流式传输数据时,性能要好得多。我不确定为什么会这样。

上下文和(半)细节:
我们使用在 C++ 中创建的合成图像作为样本数据进行训练。数据合成程序是线程安全的,因此可以通过并发生成来提高吞吐量。Python (CPython) 有其讨厌的 GIL,因此我们在 C++ 中实现了一个缓冲暂存区。这个暂存区由多个生产者填充,并通过我们的 python 生成器(C++ -> python 转换使用ctypes)使用。

这个程序结构背后的意图是通过最小化在 python 生成器中完成的工作来提高数据 IO 的速度。

在测试 python 生成器的原始吞吐量时,这在简单的情况下非常有效。在一个简单的 Tensorflow 程序的上下文中工作时,它也能正常工作,该程序只是从这个生成器创建一个数据集并迭代 N 个示例。

然而,当插入我们的实际训练管道时,缓冲生成器的功能似乎远不合适。

生产者线程在 Tensorflow 训练程序的生命周期中非常快速地填充缓冲区,并且大部分时间都在等待(不是忙于等待)。我的理解是,一旦缓冲区已满,此管道中的 IO 将变得微不足道,因为 python 生成器需要做的就是从其良好的预格式化样本供应中提取一个样本,并将yield其转移到 Tensorflow 运行时(通过tf.data.Dataset.from_generator) .

问题是当这个生成器插入到我们实际的Tensorflow 训练程序中时,吞吐量大大低于过去使用静态数据或数据生成器的无缓冲版本(缓冲版本大约是 40%在非玩具 TF 运行时使用时的无缓冲版本)。无论生产者线程的数量如何,缓冲暂存区的吞吐量都很糟糕。

我目前不知道这种行为的来源,我想知道是否有人对可能发生的事情有任何见解。据我所知,生成器函数运行正常,适当数量的线程在适当的时间做适当数量的工作。然而,在 Tensorflow 方面,我对事情的运作方式不太清楚。

其他信息:
我认为这可能与 Tensorflow Dataset API 提供的以下注释有关(副标题:from_generator):

注意:如果生成器依赖于可变的全局变量或其他外部状态,请注意运行时可能会多次调用生成器(为了支持重复数据集)以及在调用 Dataset.from_generator() 和生成生成器的第一个元素。改变全局变量或外部状态可能会导致未定义的行为,我们建议您在调用 Dataset.from_generator() 之前显式缓存生成器中的任何外部状态。

有一些与这个 python 生成器相关的外部状态,看它本质上是如何从 MPSC 缓冲区中弹出的(它正在被单独的线程中填充),但我不太明白这种状态如何/为什么特别会导致问题。

我认识到自定义 Tensorflow 操作是另一种选择,但如果可能的话,我也想找出这个问题的根本原因。

如果需要,我可以包含一些源代码,但我更担心底层方法可能存在缺陷。

我的问题是:
这个程序设计是否存在固有问题,或者这种行为可能只是我的代码中的一些错误的结果?

感谢您的帮助,如果需要更多详细信息,请告诉我。

标签: pythonmultithreadingtensorflowtensorflow-datasets

解决方案


推荐阅读