【问题标题】:Counter on GPU insanely slow compared to default counter?与默认计数器相比,GPU 上的计数器非常慢?
【发布时间】:2016-03-01 15:08:03
【问题描述】:

编辑 -- 见底部的编辑,GPU 上的 tensorflow 对于增加大型计数器向量的速度要快得多。

我正在尝试查看使用 GPU 是否会给我带来任何速度优势,并且以下程序仅计算 200,000 次,一次使用 tensor-flow 和 GPU,另一次使用 plain-ol-python。张量流循环需要超过 14 秒才能运行,而普通的 ol python 只需要 0.013 秒?我究竟做错了什么?代码如下:

#!/usr/bin/env python
import tensorflow as tf
import sys, time
# Create a Variable, that will be initialized to the scalar value 0.
state = tf.Variable(0, name="counter")                                                                                            

MAX=10000

# Create an Op to add one to `state`.
one = tf.constant(1)
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# Variables must be initialized by running an `init` Op after having

# launched the graph.  We first have to add the `init` Op to the graph.
init_op = tf.initialize_all_variables()

if __name__ == '__main__' :
    # Launch the graph and run the ops.
    with tf.Session() as sess:
        # Run the 'init' op
        sess.run(init_op)
        # Print the initial value of 'state'
        print sess.run(state)
        # Run the op that updates 'state' and print 'state'.
        print "starting ..."
        t0 = time.time()
        for _ in range(int(sys.argv[1]) if len(sys.argv) > 1 else MAX):
            sess.run(update)

        print str(sess.run(state)) + str(time.time() - t0) 

        count = 0 
        print "starting ..."
        t0 = time.time()
        for _ in range(int(sys.argv[1]) if len(sys.argv) > 1 else MAX):
            count+=1

        print str(count) + str(time.time() - t0) 

输出这个

$ ./helloworld.py 200000
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 8
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:888] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:88] Found device 0 with properties: 
name: GeForce GTX 970
major: 5 minor: 2 memoryClockRate (GHz) 1.3165
pciBusID 0000:01:00.0
Total memory: 4.00GiB
Free memory: 3.69GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:112] DMA: 0 
I tensorflow/core/common_runtime/gpu/gpu_init.cc:122] 0:   Y 
I tensorflow/core/common_runtime/gpu/gpu_device.cc:643] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 970, pci bus id: 0000:01:00.0)
I tensorflow/core/common_runtime/gpu/gpu_region_allocator.cc:47] Setting region size to 3649540096
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
0
starting ...
20000014.444382906
starting ...
2000000.0131969451904

编辑——在建议更改为计数器向量之后,gpu 上的 tensorflow 速度快得令人难以置信。

每个向量有 10,000 个计数器:

#!/usr/bin/env python
import tensorflow as tf
import sys, time

CSIZE=10000
# Create a Variable, that will be initialized to the scalar value 0.
state = tf.Variable([0 for x in range(CSIZE)], name="counter")

MAX=1000

# Create an Op to add one to `state`.
one = tf.constant([1 for x in range(CSIZE)])
new_value = tf.add(state, one)
update = tf.assign(state, new_value)

# Variables must be initialized by running an `init` Op after having

# launched the graph.  We first have to add the `init` Op to the graph.
init_op = tf.initialize_all_variables()

if __name__ == '__main__' :
    # Launch the graph and run the ops.
    with tf.Session() as sess:
        # Run the 'init' op
        sess.run(init_op)
        # Print the initial value of 'state'
        print sess.run(state)
        # Run the op that updates 'state' and print 'state'.
        print "starting ..."
        t0 = time.time()
        for _ in range(int(sys.argv[1]) if len(sys.argv) > 1 else MAX):
            sess.run(update)

        print str(sess.run(state)) + str(time.time() - t0) 

        counters = [0 for x in range(CSIZE)]                                                                                      
        print "starting ..."
        t0 = time.time()
        for _ in range(int(sys.argv[1]) if len(sys.argv) > 1 else MAX):
            for x in range(0, len(counters)) :
                counters[x]+=1

        print str(counters[0]) + ", " +  str(time.time() - t0) 

输出:

$ ./helloworld.py                                                                                                           127 ↵
I tensorflow/core/common_runtime/local_device.cc:25] Local device intra op parallelism threads: 8
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:888] successful NUMA node read from SysFS had negative value (-1), but there must be at least one NUMA node, so returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_init.cc:88] Found device 0 with properties: 
name: GeForce GTX 970
major: 5 minor: 2 memoryClockRate (GHz) 1.3165
pciBusID 0000:01:00.0
Total memory: 4.00GiB
Free memory: 3.69GiB
I tensorflow/core/common_runtime/gpu/gpu_init.cc:112] DMA: 0 
I tensorflow/core/common_runtime/gpu/gpu_init.cc:122] 0:   Y 
I tensorflow/core/common_runtime/gpu/gpu_device.cc:643] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 970, pci bus id: 0000:01:00.0)
I tensorflow/core/common_runtime/gpu/gpu_region_allocator.cc:47] Setting region size to 3645083648
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
[0 0 0 ..., 0 0 0]
starting ...
[10000 10000 10000 ..., 10000 10000 10000]0.997926950455
starting ...
10000, 9.66100215912

使用 100,000 个计数器,输出为:

I tensorflow/core/common_runtime/gpu/gpu_region_allocator.cc:47] Setting region size to 3653734400
I tensorflow/core/common_runtime/local_session.cc:45] Local session inter op parallelism threads: 8
[0 0 0 ..., 0 0 0]
starting ...
[10000 10000 10000 ..., 10000 10000 10000]1.57860684395
starting ...
^CTraceback (most recent call last):
  File "./helloworld.py", line 40, in <module>
    for x in range(0, len(counters)) :
KeyboardInterrupt

普通的 ol python 花了一分钟,直到我放弃

【问题讨论】:

  • GPU 是为成千上万的并发操作而设计的并行机器。您似乎正在执行串行操作。尝试增加一百万个计数器的向量。
  • 谢谢伙计,这真的很好用!我做到了,所以我有 10,000 个计数器的向量,结果是 tensorflow 为 1.02350 秒,普通 ol python 为 9.7290。

标签: python cuda tensorflow


【解决方案1】:

从某种意义上说,与必须执行的指令数量相比,这两个程序都“出奇地”慢。单元素计数器在 14.4 秒内使用 200,000 次对 sess.run() 的调用执行 200,000 条递增指令。向量计数器在 0.99 秒内执行 100,000,000 条递增指令,使用 10,000 次对 sess.run() 的调用。如果您用 C 编写这些程序,您会发现每次计数器增量最多需要几纳秒,那么这些时间花在了哪里?

TensorFlow 会在每个步骤中施加一些开销,每次调用 Session.run() 大约需要几微秒。这是一个known issue,这是团队正在努力减少的问题,但对于大多数通常在一个步骤中运行的神经网络算法来说,这很少是一个问题。开销可以细分如下:

  • 每步调度开销: TensorFlow 会话 API 是基于字符串的,因此必须进行一些字符串操作和散列以识别要在每个步骤中运行的正确子图。这涉及到一些 Python 和一些 C++ 代码。
  • Per-op 调度开销:这是用 C++ 实现的,涉及设置上下文和调度 TensorFlow 内核。您的计数器基准中有三个操作(VariableOpAddAssign)。
  • GPU 内核调度开销:向 GPU 调度内核涉及调用 GPU 驱动程序的内核入口。
  • GPU 复制开销: 可能令人惊讶的是,sess.run(update) 会将结果从 GPU 复制回来,因为update 是一个Tensor 对象(对应于赋值的结果),其值将从调用中返回。

您可以尝试一些方法来加快这两个版本的代码。

  • 使用 state.assign_add(one) 而不是单独的 tf.addtf.assign 操作将减少每个操作的调度开销(并且还可以进行更有效的就地添加)。

    李>
  • 调用sess.run(update.op) 将避免在每个步骤中将副本返回给客户端。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-01-09
    • 2015-03-29
    • 2020-07-24
    • 2012-04-16
    • 1970-01-01
    • 2014-10-06
    • 2021-02-13
    相关资源
    最近更新 更多