我尝试运行一个玩具示例,使用分布式TensorFlow进行一些矩阵乘法和加法。
我的目标是计算(A^n + B^n)
A[,]
和B[,]
在哪里LxL
矩阵。
我在公共云上使用 2 台机器在一台机器上计算A^n
,在第二台机器上B^n
,而不是在第一台机器上再次添加。
当机器只有 CPU 时 - 我的脚本工作得很好。
当两者都有 GPU 时 - 它未能在合理的时间内运行!它有一个巨大的延迟...
我的问题- 我在脚本中做错了什么?
请注意,对于机器 2 (task:1
( 我使用了server.join()
并且我使用机器 1 (task:0
( 作为此内部图中的客户端。
#------------------------------------------------------------------
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. 在这里(。
很难-- 提前感谢运行 + 发布更新的事实。
用定量支持的语句继续 ATM,但是,我的直觉怀疑在于这一点:
def matpow( M, n ):
return M if ( n < 1 ) else tf.matmul( M, matpow( M, n - 1 ) )
它使用递归,对于 GPU 交叉编译器/汇编器分析器来说可能太深了,并且给定张量的规模,GPU SMX 对于数学密集内核微代码"快速",SMX 本地SM_registers(延迟约为22GPU_CLK
秒(好吧,可能只有 8 秒,如果智能优化以从 LRU 对齐良好的 SM_L1 缓存行中获取(将不得不溢出到 global_MEMORY(因为每个 SM 都有机会存储小于 1KB 在重复内存访问延迟最友好的SM_Registers中,但 matmul(( 从不重用矩阵的任何单元格,因此延迟隐藏永远不会低于每次global_MEMORY访问 +[PSPACE]
缩放。(,突然遇到大约600+GPU_CLK
秒的延迟损失。
虽然这个HPCmatmul()
的叙述动画提到了典型的 CPU/Lx-缓存/内存层次结构,但为什么任何O(N^3)
处理都必须在 GPU-s 上变得非常慢,因为N
大于可以容纳SM_registers的信息仍然很容易看到(正如你所看到的,想象一下你所有的缓存友好性在递归matpow()
中丢失了(。
GPU 内核为小规模静止 SM-局部卷积获得最佳结果(在这个 SMX 局部性允许良好的 [Data:SMX-local SM_REG] 对齐(并且需要发生零交叉 SMX 通信(这在 matmul(( 处理中不是这种情况,大于 ~ 7 x 7 矩阵,可以适合 SM_REG 硅,高于此的任何内容都必须启动超级智能模板对齐体操, 如果努力支付恰好 GPU 本地 memIO 延迟的最低必要总和(故事继续前进,如果糟糕的 host2dev/dev2host IO 发生,以及更多执行性能无法控制的地方很差((。
与典型的图像专用内核相比,即使是单个matmul( A, A )
的延迟成本也突然变得非常糟糕。 (当然,有先进的技术,如何绕过这个专门的硅限制,但即使matmul()
是顶级HPC-块-矩阵-操作的大师,一旦递归调用进入舞台,它也不会成为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