【问题标题】:Benchmark of HowTo: Reading DataHowTo 的基准:读取数据
【发布时间】:2016-10-03 20:58:40
【问题描述】:

我使用的是 tensorflow 0.10,我正在对 official HowTo on reading data 中的示例进行基准测试。本 HowTo 使用相同的 MNIST 示例说明了将数据移动到 tensorflow 的不同方法。

我对结果感到惊讶,我想知道是否有人有足够的底层理解来解释正在发生的事情。

在 HowTo 中基本上有 3 种读取数据的方法:

  • Feeding:在 python 中构建小批量并使用 sess.run(..., feed_dict={x: mini_batch}) 传递它
  • Reading from files:使用tf 操作打开文件并创建小批量。 (绕过在 python 中处理数据。)
  • Preloaded data:将所有数据加载到单个tf 变量或常量中,并使用tf 函数将其分解为小批量。变量或常量被固定到 cpu,而不是 gpu。

我用来运行基准测试的脚本可以在 tensorflow 中找到:

除了最后两个之外,我在未修改的情况下运行了这些脚本,因为它们崩溃了——至少对于 0.10 版——除非我添加额外的 sess.run(tf.initialize_local_variables())

主要问题

在 GTX1060 上运行 100 个示例,执行 100 个小批量所需的时间:

  • Feeding: ~0.001 s
  • Reading from files: ~0.010 s
  • Preloaded data (constant): ~0.010 s
  • Preloaded data (variable): ~0.010 s

这些结果让我很惊讶。我原以为Feeding 是最慢的,因为它几乎可以在 python 中完成所有操作,而其他方法使用较低级别的 tensorflow/C++ 来执行类似的操作。这与我的预期完全相反。有谁明白这是怎么回事?

次要问题

我可以访问另一台装有 Titan X 和旧版 NVidia 驱动程序的机器。相对结果大致与上述一致,除了Preloaded data (constant) 非常缓慢,单个小批量需要很多秒。

这是一个已知问题,即性能会因硬件/驱动程序而有很大差异吗?

【问题讨论】:

  • 顺便说一句,这里是如何使它快速 - github.com/yaroslavvb/stuff/commit/… 。更多详情github.com/tensorflow/tensorflow/issues/…
  • 这种缓慢可能是特定于小型模型的。这里的问题是数据计算线程缺乏数据。由于计算运行速度如此之快,数据预加载线程(您可以使用 tf.batch(...,num_threads=10) 进行更多操作)没有机会抢占它并加载更多数据

标签: python tensorflow


【解决方案1】:

10 月 9 日更新速度变慢是因为计算运行速度太快,Python 无法抢占计算线程并安排预取线程。主线程中的计算需要 2 毫秒,显然这对于​​预取线程来说太少了,无法获取 GIL。预取线程具有较大的延迟,因此总是可以被计算线程抢占。因此,计算线程运行所有示例,然后将大部分时间花在 GIL 上,因为一些预取线程被调度并将单个示例排入队列。解决方案是增加 Python 线程的数量,增加队列大小以适应整个数据集,启动队列运行器,然后暂停主线程几秒钟,让队列运行器预先填充队列。

旧物

这出奇的慢。

这看起来是某种特殊情况,使得最后 3 个示例不必要地变慢(大部分精力都用于优化 ImageNet 等大型模型,因此 MNIST 没有得到太多关注)。

您可以通过获取时间表来诊断问题,如here 所述

这里are 3 个启用时间线收集的示例。

这是feed_dict 实施的时间表

需要注意的重要一点是 matmul 占用了大量时间,因此读取开销并不显着

现在是reader 实施的时间表

您可以看到 QueueDequeueMany 上的操作出现瓶颈,耗时高达 45 毫秒。

如果放大,您会看到一堆微小的 MEMCPY 和 Cast 操作,这表明某些操作仅是 CPU (parse_single_example),并且出队必须安排多个独立的 CPU->GPU 传输

对于下面禁用 GPU 的 var 示例,我看不到很小的操作,但 QueueDequeueMany 仍然需要超过 10 毫秒。时间似乎与批量大小成线性关系,所以那里有一些基本的缓慢。归档#4740

【讨论】:

    【解决方案2】:

    雅罗斯拉夫很好地解决了这个问题。对于小型模型,您需要加快数据导入。一种方法是使用 Tensorflow 函数 tf.TFRecordReader.read_up_to,该函数在每个 session.run() 调用中读取多条记录,从而消除多次调用造成的额外开销。

    enqueue_many_size = SOME_ENQUEUE_MANY_SIZE
    reader = tf.TFRecordReader(options = tf.python_io.TFRecordOptions(tf.python_io.TFRecordCompressionType.ZLIB))
    _, queue_batch = reader.read_up_to(filename_queue, enqueue_many_size)
    batch_serialized_example = tf.train.shuffle_batch(
        [queue_batch],
        batch_size=batch_size,
        num_threads=thread_number,
        capacity=capacity,
        min_after_dequeue=min_after_dequeue,
        enqueue_many=True)
    

    SO question 也解决了这个问题。

    【讨论】:

      【解决方案3】:

      主要问题是为什么带有预加载数据(常量)的示例 examples/how_tos/reading_data/fully_connected_preloaded.py 在使用 GPU 时比其他数据加载示例代码慢得多。

      我遇到了同样的问题,fully_connected_preloaded.py 在我的 Titan X 上出乎意料地慢。问题是整个数据集是预先加载到 CPU 上的,而不是 GPU 上。

      首先,让我分享一下我最初的尝试。我应用了 Yaroslav 的以下性能技巧。

      • tf.train.slice_input_producer设置capacity=55000。(在我的例子中,55000是MNIST训练集的大小)
      • num_threads=5 设置为tf.train.batch
      • capacity=500 设置为tf.train.batch
      • time.sleep(10)放在tf.train.start_queue_runners之后。

      但是,每批的平均速度保持不变。我尝试使用timeline 可视化进行分析,但仍然以QueueDequeueManyV2 为主导。

      问题是the line 65fully_connected_preloaded.py。以下代码将整个数据集加载到 CPU,仍然为 CPU-GPU 数据传输提供了瓶颈。

      with tf.device('/cpu:0'):
          input_images = tf.constant(data_sets.train.images)
          input_labels = tf.constant(data_sets.train.labels)
      

      因此,我切换了设备分配。

      with tf.device('/gpu:0')
      

      然后我每批次都获得了 x100 的加速。

      注意:

      1. 这是可能的,因为 Titan X 有足够的内存空间来预加载整个数据集。
      2. 在原始代码 (fully_connected_preloaded.py) 中,the line 64 中的注释说“管道的其余部分是 CPU-only”。我不确定这条评论的意图。

      【讨论】:

        猜你喜欢
        • 2013-04-17
        • 2017-03-27
        • 1970-01-01
        • 1970-01-01
        • 2015-11-30
        • 2015-10-14
        • 1970-01-01
        • 2021-06-16
        • 2015-08-10
        相关资源
        最近更新 更多