python - 为什么分布式 TensorFlow 玩具示例需要太长时间?

标签 python distributed-computing tensorflow

我尝试使用分布式 TensorFlow 运行一些矩阵乘法和加法的玩具示例。

我的目标是计算 (A^n + B^n) 其中 A[,]B[,]LxL 矩阵。

我在公共(public)云上使用了 2 台机器,在一台机器上计算 A^n,在第二台机器上计算 B^n,然后在第一台机器上再次相加.

当机器只有 CPU 时 - 我的脚本运行良好。
当两者都有 GPU 时 - 它无法在合理的时间内运行!它有一个巨大的延迟...

我的问题 - 我的脚本做错了什么?

请注意,对于机器 2 (task:1),我使用了 server.join() 并且我使用了机器 1 (task:0)作为这个with-in图中的客户。

#------------------------------------------------------------------
from zmq import Stopwatch; aClk_E2E = Stopwatch(); aClk_E2E.start()
#------------------------------------------------------------------
from __future__ import print_function
import numpy as np
import tensorflow as tf
import datetime

IP_1 = '10.132.0.2';     port_1 = '2222'
IP_2 = '10.132.0.3';     port_2 = '2222'

cluster = tf.train.ClusterSpec( { "local": [ IP_1 + ":" + port_1,
                                             IP_2 + ":" + port_2
                                             ],
                                   }
                                )
server = tf.train.Server( cluster,
                          job_name   = "local",
                          task_index = 0
                          )
# server.join() # @machine2 ( task:1 )

n =    5
L = 1000  

def matpow( M, n ):
    if n < 1:                 # Abstract cases where n < 1
        return M
    else:
        return tf.matmul( M, matpow( M, n - 1 ) )

G = tf.Graph()

with G.as_default():
     with tf.device( "/job:local/task:1/cpu:0" ):
          c1 = []
          tB = tf.placeholder( tf.float32, [L, L] )     # tensor B placeholder
          with tf.device( "/job:local/task:1/gpu:0" ):
               c1.append( matpow( tB, n ) )

     with tf.device( "/job:local/task:0/cpu:0" ):
          c2 = []
          tA = tf.placeholder( tf.float32, [L, L] )     # tensor A placeholder
          with tf.device( "/job:local/task:0/gpu:0" ):
               c2.append( matpow( tA, n ) )
          sum2 = tf.add_n( c1 + c2 )
#---------------------------------------------------------<SECTION-UNDER-TEST>
t1_2 = datetime.datetime.now()
with tf.Session( "grpc://" + IP_1 + ":" + port_1, graph = G ) as sess:
     A = np.random.rand( L, L ).astype( 'float32' )
     B = np.random.rand( L, L ).astype( 'float32' )
     sess.run( sum2, { tA: A, tB: B, } )
t2_2 = datetime.datetime.now()
#---------------------------------------------------------<SECTION-UNDER-TEST>

#------------------------------------------------------------------
_ = aClk_E2E.stop()
#------------------------------------------------------------------
print( "Distributed Computation time: " + str(t2_2 - t1_2))
print( "Distributed Experiment  took: {0: > 16d} [us] End-2-End.".format( _ ) )

最佳答案

分布式计算是我们的新宇宙,或一组平行的宇宙

进入这个领域的第一步总是充满挑战。失去确定性,在单体计算之前的经验中被认为是理所当然的,许多新的挑战,在单节点进程协调中没有类似的问题,许多新的惊喜来自新数量级的分布式执行时序和分布式(协调,如果不是死锁和/或活锁)阻塞问题。

感谢您添加一些定量事实 ~ 15 秒对于 A[1000,1000];B[1000,1000];n=5 来说“太长了”——到目前为止还不错。


您是否介意添加上述建议的代码更改并在相同的真实基础设施上重新运行实验?

这将有助于开始工作的其余部分(W.I.P. 这里)。

-- THANKS IN ADVANCE to run + post updated facts.


用定量支持的陈述很难继续 ATM,但是,我的直觉怀疑是在这个:

 def matpow( M, n ):
     return  M if ( n < 1 ) else tf.matmul( M, matpow( M, n - 1 ) )

它使用递归,对于 GPU 交叉编译器/汇编器分析器和给定的张量尺度来说可能太深了,GPU SMX 对于数学上密集的内核微代码来说是“快速的”,SMX 本地 SM_registers(具有大约 22 GPU_CLK 的延迟(嗯,可能只有 8,如果智能优化以从 LRU 对齐的 SM_L1-cache-line 中获取)将不得不溢出-over to global_MEMORY(因为每个 SM 都有机会在重复内存访问延迟最友好的 SM_Registers 中存储小于 1 KB 的方式,但是 matmul() 从不重复使用矩阵的任何单元,因此延迟隐藏将永远不要低于每次 global_MEMORY 访问 + [PSPACE] 比例 .. ),突然体验大约 600+ GPU_CLK 的延迟惩罚。

While this narrated animation of HPC matmul()提到典型的 CPU/Lx-cache/内存层次结构,消息为什么任何 O(N^3) 处理必须在 GPU-s 上变得非常慢 N 超出 SM_registers 的容量仍然很容易看到(正如您所看到的,想象一下您在递归 matpow() 中失去了所有缓存友好性)。

GPU 内核在小规模 still-SM-local-convolutions 中获得最佳结果(这个 SMX-locality 允许良好的 [Data:SMX-local SM_REG] 对齐(并且零交叉 SMX-communication 需要采取放置(在 matmul() 处理任何大于 ~ 7 x 7 矩阵的情况下,情况并非如此,它可以适合 SM_REG 硅,任何高于此的都必须启动 super 智能模板对齐体操,如果努力支付最低必要仅 GPU 本地内存 IO 延迟的总和(如果 host2dev/dev2host IO 发生不佳,以及执行性能无法控制地差的更多地方,故事会继续进行)。

与典型的图像专用内核相比,即使是单个 matmul( A, A ) 的延迟成本也突然变得非常低。 (当然,有一些先进的技术可以绕过这种专用硅限制,但即使 matmul() 是顶级 HPC-block-matrix-ops 的大师,它也会以免作为 naive-matmul() 一旦递归调用出现 - 这甚至会杀死聪明的技巧,因为没有 [SPACE] 对于“堆栈”-中间值,自动生成的内核代码将为此付出巨大的 [TIME] 惩罚..即使对于这么小的尺度,比如 1000x1000) .

   Category                     GPU
   |                            Hardware
   |                            Unit
   |                            |            Throughput
   |                            |            |               Execution
   |                            |            |               Latency
   |                            |            |               |                  PTX instructions                                                      Note 
   |____________________________|____________|_______________|__________________|_____________________________________________________________________|________________________________________________________________________________________________________________________
   Load_shared                  LSU          2               +  30              ld, ldu                                                               Note, .ss = .shared ; .vec and .type determine the size of load. Note also that we omit .cop since no cacheable in Ocelot
   Load_global                  LSU          2               + 600              ld, ldu, prefetch, prefetchu                                          Note, .ss = .global; .vec and .type determine the size of load. Note, Ocelot may not generate prefetch since no caches
   Load_local                   LSU          2               + 600              ld, ldu, prefetch, prefetchu                                          Note, .ss = .local; .vec and .type determine the size of load. Note, Ocelot may not generate prefetch since no caches
   Load_const                   LSU          2               + 600              ld, ldu                                                               Note, .ss = .const; .vec and .type determine the size of load
   Load_param                   LSU          2               +  30              ld, ldu                                                               Note, .ss = .param; .vec and .type determine the size of load
   |                            |                              
   Store_shared                 LSU          2               +  30              st                                                                    Note, .ss = .shared; .vec and .type determine the size of store
   Store_global                 LSU          2               + 600              st                                                                    Note, .ss = .global; .vec and .type determine the size of store
   Store_local                  LSU          2               + 600              st                                                                    Note, .ss = .local; .vec and .type determine the size of store
   Read_modify_write_shared     LSU          2               + 600              atom, red                                                             Note, .space = shared; .type determine the size
   Read_modify_write_global     LSU          2               + 600              atom, red                                                             Note, .space = global; .type determine the size
   |                            |                              
   Texture                      LSU          2               + 600              tex, txq, suld, sust, sured, suq
   |                            |                              
   Integer                      ALU          2               +  24              add, sub, add.cc, addc, sub.cc, subc, mul, mad, mul24, mad24, sad, div, rem, abs, neg, min, max, popc, clz, bfind, brev, bfe, bfi, prmt, mov
   |                            |                                                                                                                     Note, these integer inst. with type = { .u16, .u32, .u64, .s16, .s32, .s64 };
   |                            |                              
   Float_single                 ALU          2               +  24              testp, copysign, add, sub, mul, fma, mad, div, abs, neg, min, max     Note, these Float-single inst. with type = { .f32 };
   Float_double                 ALU          1               +  48              testp, copysign, add, sub, mul, fma, mad, div, abs, neg, min, max     Note, these Float-double inst. with type = { .f64 };
   Special_single               SFU          8               +  48              rcp, sqrt, rsqrt, sin, cos, lg2, ex2                                  Note, these special-single with type = { .f32 };
   Special_double               SFU          8               +  72              rcp, sqrt, rsqrt, sin, cos, lg2, ex2                                  Note, these special-double with type = { .f64 };
   |                                                           
   Logical                      ALU          2               +  24              and, or, xor, not, cnot, shl, shr
   Control                      ALU          2               +  24              bra, call, ret, exit
   |                                                           
   Synchronization              ALU          2               +  24              bar, member, vote
   Compare & Select             ALU          2               +  24              set, setp, selp, slct
   |                                                           
   Conversion                   ALU          2               +  24              Isspacep, cvta, cvt
   Miscellanies                 ALU          2               +  24              brkpt, pmevent, trap
   Video                        ALU          2               +  24              vadd, vsub, vabsdiff, vmin, vmax, vshl, vshr, vmad, vset

关于python - 为什么分布式 TensorFlow 玩具示例需要太长时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45723213/

相关文章:

python - 如何在 python 中执行批处理文件后关闭命令提示符窗口?

python - 值错误 : Cannot assign in django

python - 使用 Pandas 进行机器学习数据预处理

java - 使用 Zookeeper 和 Thrift 负载均衡服务

python - Keras 深度学习模型在训练中始终给出相同的帐户

tensorflow - 转换为张量后,参差不齐的张量没有 len()

python - 将包含多行字符串的 Pandas 系列行拆分为单独的行

python - 吉普 错误!堆栈错误 : Can't find Python executable "C:\Users\Admin\Anaconda3\python.EXE", 您可以设置 PYTHON 环境变量

java - Zookeeper cfg 文件 - 为什么有多个端口?

java - 网格计算中用于发现客户端的软件