HowTo的基准:阅读数据

时间:2016-10-03 20:58:40

标签: python tensorflow

我正在使用tensorflow 0.10,我正在对official HowTo on reading data中的示例进行基准测试。本文介绍了使用相同的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)这是一个灾难性的缓慢,单个小批量需要几秒钟。

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

3 个答案:

答案 0 :(得分:5)

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

旧东西

这太慢了。

这看起来有些特殊情况使得最后3个示例不必要地变慢(大部分工作都用于优化像ImageNet这样的大型模型,因此MNIST没有得到足够多的关注)。

您可以按照here

所述的时间表来诊断问题

此处are启用了时间轴收集的示例中的3个。

这是feed_dict实施的时间表

enter image description here

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

现在是reader实施的时间表 enter image description here

您可以看到QueueDequeueMany上的操作存在瓶颈,需要高达45毫秒。

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

对于下面禁用GPU的var示例,我看不到微小的操作,但QueueDequeueMany仍然需要10毫秒。时间似乎与批量大小呈线性关系,因此存在一些根本性的缓慢。提起#4740 enter image description here

答案 1 :(得分: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中解决了。

答案 2 :(得分:1)

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

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

首先,让我分享我最初的尝试。我应用了雅罗斯拉夫的以下表现提示。

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

但是,每批的平均速度保持不变。我尝试了timeline可视化进行性能分析,但仍然有QueueDequeueManyV2支配。

问题是fully_connected_preloaded.py the line 65。以下代码将整个数据集加载到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"。我不确定这条评论的意图。