Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swin graph 3d 并行,打开 acc grad 报错 #232

Open
Ldpe2G opened this issue Mar 29, 2022 · 71 comments · Fixed by Oneflow-Inc/oneflow#8052
Open

Swin graph 3d 并行,打开 acc grad 报错 #232

Ldpe2G opened this issue Mar 29, 2022 · 71 comments · Fixed by Oneflow-Inc/oneflow#8052
Assignees
Labels
bug Something isn't working

Comments

@Ldpe2G
Copy link
Collaborator

Ldpe2G commented Mar 29, 2022

实验分支:#215

文件 swin_cifar100.py 关键配置

train.train_micro_batch_size = 8
train.num_accumulation_steps = 2
train.test_micro_batch_size = 16

# parallel strategy settings
train.dist.data_parallel_size = 2
train.dist.tensor_parallel_size = 2
train.dist.pipeline_parallel_size = 2
train.dist.pipeline_num_layers = sum(model.depths)
train.output_dir="./output"

# Set fp16 ON
train.amp.enabled = False
train.activation_checkpoint.enabled = False
graph.enabled = True
bash tools/train.sh tools/train_net.py configs/swin_cifar100.py 8

报错信息

F20220329 17:22:55.548020 41348 task_node.cpp:385] Check failed: lbi2data_regst.size() == lbis_.size() (1 vs. 0)

 TaskEdge lbi and regst NOT match. TaskEdge: edge_id = 139009 From: [System-GradientAccumulation-VariableRepeat-model.layers.3.blocks.1.attn.relative_position_bias_table-500] To: [kBoxingZeros\n5:84955136\n178163833374140]
*** Check failure stack trace: ***
    @     0x7febda2b92ea  (unknown)
    @     0x7febda2b95d2  (unknown)
    @     0x7febda2b8e57  (unknown)
    @     0x7febda2bb9c9  (unknown)
    @     0x7febd3755466  oneflow::TaskEdge::CheckRegstLbiValid()
    @     0x7febd37b3e1b  oneflow::Compiler::Compile()
    @     0x7febd327fd17  oneflow::NNGraph::CompileAndInitRuntime()
    @     0x7febd1c43f6f  (unknown)
    @     0x7febd16d3344  (unknown)
    @     0x55f88f4caed4  _PyCFunction_FastCallDict
    @     0x55f88f552c4e  call_function
    @     0x55f88f5752ca  _PyEval_EvalFrameDefault
    @     0x55f88f54bdc4  _PyEval_EvalCodeWithName
    @     0x55f88f54d358  _PyFunction_FastCallDict
    @     0x55f88f4cb29f  _PyObject_FastCallDict
    @     0x55f88f4cfda3  _PyObject_Call_Prepend
    @     0x55f88f4cacde  PyObject_Call
    @     0x55f88f576952  _PyEval_EvalFrameDefault
    @     0x55f88f54bdc4  _PyEval_EvalCodeWithName
    @     0x55f88f54d358  _PyFunction_FastCallDict
    @     0x55f88f4cb29f  _PyObject_FastCallDict
    @     0x55f88f4cfda3  _PyObject_Call_Prepend
    @     0x55f88f4cacde  PyObject_Call
    @     0x55f88f523971  slot_tp_call
    @     0x55f88f4cacde  PyObject_Call
@Ldpe2G Ldpe2G added the bug Something isn't working label Mar 29, 2022
@chengtbf
Copy link

这个 Check 是我加的,去年年初重构移除 Logical Graph 的时候搞的。理论上不会出现。我看看代码回想一下逻辑 😂

@chengtbf
Copy link

德澎你这个测试,是在哪台机器上做的? @Ldpe2G 金山还是类脑

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

德澎你这个测试,是在哪台机器上做的? @Ldpe2G 金山还是类脑

之前是在金山上做的

@chengtbf
Copy link

验证这个分支:

是否解决上述问题。

这个 BUG 的原因是: swin Variable Op 后面有一个 B2P 的 boxing,该 boxing 会插入 zero boxing task node。但是在 :NaiveB2PSubTskGphBuilder 中, B2P 构造的 task edge 没有 add lbi。由于之前的 case 里没有 B2P 的单测,导致个 BUG 隐藏到现在才暴露。

@liujuncheng @Ldpe2G @strint @leaves-zwx

@chengtbf
Copy link

不过我有一个问题,B2P 是一个不太常见的 boxing, swin 开 3-D 并行,Variable 后面跟一个 B2P 的消费,是否符合预期? @Ldpe2G @strint @leaves-zwx @L1aoXingyu

@leaves-zwx
Copy link
Collaborator

我理解目前应该不存在 B2P 的实际应用场景

@strint
Copy link
Collaborator

strint commented Apr 19, 2022

不符合预期,需要看看是怎么来的~

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

得看下是哪个模块导致这个

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

这组配置可以跑起来

train.train_micro_batch_size = 8
train.num_accumulation_steps = 2
train.test_micro_batch_size = 16

这组配置虽然不会报错,但是会卡住,有几张卡利用率 100%,其他为0%

train.train_micro_batch_size = 32
train.num_accumulation_steps = 4
train.test_micro_batch_size = 128

num_accumulation_steps 的设置是不是不能超过 网络流水的 stage 数量?8卡3d并行是分成两个stage。

@chengtbf
Copy link

num_accumulation_steps 的设置是不是不能超过 网络流水的 stage 数量?8卡3d并行是分成两个stage。

不是,相反,grad acc 的次数,应该是至少 stage 数量的 2 倍。

@chengtbf
Copy link

这组配置可以跑起来

train.train_micro_batch_size = 8
train.num_accumulation_steps = 2
train.test_micro_batch_size = 16

这组配置虽然不会报错,但是会卡住,有几张卡利用率 100%,其他为0%

train.train_micro_batch_size = 32
train.num_accumulation_steps = 4
train.test_micro_batch_size = 128

那可能是网络结构上有问题。 @leaves-zwx 文骁辅助德澎 debug 一下吧。 这个跟中兴那边的 swin-T 有交集。

@leaves-zwx

This comment was marked as duplicate.

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

这组配置虽然不会报错,但是会卡住,有几张卡利用率 100%,其他为0%

8卡3D并行也就是 2+2+2 吗?这种现象很大可能跟 nccl 启动顺序不一致所致。nccl_use_compute_stream 这个选项开启了吗?

是的,我看下

@chengtbf
Copy link

这组配置虽然不会报错,但是会卡住,有几张卡利用率 100%,其他为0%

8卡3D并行也就是 2+2+2 吗?这种现象很大可能跟 nccl 启动顺序不一致所致。nccl_use_compute_stream 这个选项开启了吗?

这个在 libai 里是默认开启的。 @L1aoXingyu

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

我这边试验了一下,关掉能跑起来

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented Apr 19, 2022

第一步,先确认各 rank 运行卡在什么位置(也就是什么 op 上),基本上有2种方法:

  • log 大法,在 kernel 的关键函数上(比如 forward)添加合适的 debug code,LOG(INFO) 打印 op 的信息(最重要肯定是 op_name),运行卡住后,从各rank不同的 INFO 文件里面查看都运行到什么 kernel 了
  • gdb 大法,运行 oneflow distributed 卡住后,使用 gdb attach -p pid 进入相应的进程,pid 可以从 *.INFO 的名字中查看到,也可以通过 ps aux | grep ... 来搜索出来。进入 gdb 后,主要思路是找到卡住的线程,但我们有100个左右的线程。但上面的报错表现来看,最有可能的原因就是 nccl 卡住,所以我们要找 nccl 所在线程。如果 nccl_use_compute_stream 为开启,则 nccl 在单独的 nccl stream 线程中,如果 nccl_use_compute_stream 未开启,则 nccl 在 compute stream 的线程中。在 gdb 中我们可以通过 info thread 来打印所有的线程信息,这时我们要找到我们想要的线程,需要知道线程的 LWP,我们可以在创建的线程的地方增加 debug code 来打印出不同的 stream 所对应的线程号。

简单的说就是先确认各 rank 各 stream 卡在何处。

@chengtbf
Copy link

我怎么记得这个 BUG 之前出现过。。。 上次是触发 1024 的上限,导致异步变同步死锁的 @leaves-zwx 😂

@chengtbf
Copy link

如果减少 Transformer layer 的层数会跑起来吗

@leaves-zwx
Copy link
Collaborator

我怎么记得这个 BUG 之前出现过。。。 上次是触发 1024 的上限,导致异步变同步死锁的

偏序执行不一定唯一是 nccl 调用顺序不同的原因

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 19, 2022

void Kernel::Launch(KernelContext* ctx) const {
  LOG(INFO) << "before: " << this->kernel_conf_.op_attribute().op_conf().name(); 
  ctx->WillForward(ctx, this);
  Forward(ctx);
  ctx->DidForward(ctx, this);
  LOG(INFO) << "after: " << this->kernel_conf_.op_attribute().op_conf().name(); 
}

所有 Log 文件:

https://oneflow-static.oss-cn-beijing.aliyuncs.com/log.tar.gz

@leaves-zwx

This comment was marked as outdated.

@Ldpe2G

This comment was marked as resolved.

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 20, 2022

I20220419 11:41:22.487932 2724642 eager_boxing_logger.cpp:42] Boxing route: symmetric-acyclic-nd-sbp-to-nd-sbp -> naive-b-to-1 -> naive-1-to-1 -> naive-1-to-p -> symmetric-acyclic-nd-sbp-to-nd-sbp
I20220419 11:41:22.488044 2724642 eager_boxing_logger.cpp:43] Logical shape: (256,100)
I20220419 11:41:22.488057 2724642 eager_boxing_logger.cpp:44] Altered state of sbp: (S(0), B) -> (B, B) -> (B, B) -> (B, B) -> (P, P) -> (S(0), B)
I20220419 11:41:22.488121 2724642 eager_boxing_logger.cpp:45] Altered state of placement: oneflow.placement(type="cuda", ranks=[[0, 1], [2, 3]]) -> oneflow.placement(type="cuda", ranks=[[0, 1], [2, 3]]) -> oneflow.placement(type="cuda", ranks=[[0]]) -> oneflow.placement(type="cuda", ranks=[[4]]) -> oneflow.placement(type="cuda", ranks=[[4, 5], [6, 7]]) -> oneflow.placement(type="cuda", ranks=[[4, 5], [6, 7]])

看 rank 0 LOG 的时候发现这一段,包含了 B -> P 的 boxing

@strint
Copy link
Collaborator

strint commented Apr 20, 2022

看起来是的

I20220419 11:41:22.487932 2724642 eager_boxing_logger.cpp:42] Boxing route: symmetric-acyclic-nd-sbp-to-nd-sbp 
-> naive-b-to-1 
-> naive-1-to-1 
-> naive-1-to-p // ?
-> symmetric-acyclic-nd-sbp-to-nd-sbp

I20220419 11:41:22.488044 2724642 eager_boxing_logger.cpp:43] Logical shape: (256,100)

I20220419 11:41:22.488057 2724642 eager_boxing_logger.cpp:44] Altered state of sbp: (S(0), B) 
-> (B, B) 
-> (B, B)
-> (B, B) 
-> (P, P)  // (S(0), S(0)) 是不是更好?
-> (S(0), B)

I20220419 11:41:22.488121 2724642 eager_boxing_logger.cpp:45] Altered state of placement: 
oneflow.placement(type="cuda", ranks=[[0, 1], [2, 3]]) 
-> oneflow.placement(type="cuda", ranks=[[0, 1], [2, 3]]) 
-> oneflow.placement(type="cuda", ranks=[[0]]) 
-> oneflow.placement(type="cuda", ranks=[[4]]) 
-> oneflow.placement(type="cuda", ranks=[[4, 5], [6, 7]])  // ? 
-> oneflow.placement(type="cuda", ranks=[[4, 5], [6, 7]])

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 20, 2022

System-GradientAccumulation-VariableRepeat-model.layers.1.blocks.0.attn.relative_position_bias_table-366,model.layers.1.blocks.0.attn-gather_nd-162_grad,{0:cuda:0-0 1:cuda:1-1 2:cuda:2-2 3:cuda:3-3 (2 2)},{0:cuda:0-0 1:cuda:1-1 2:cuda:2-2 3:cuda:3-3 (2 2)},(B, B),(P, P),System-GradientAccumulation-VariableRepeat-model.layers.1.blocks.0.attn.relative_position_bias_table-366/out_0,kFloat,(169 6),NaiveB2PSubTskGphBuilder,-

看 boxing cvs 里面是有 B->P 的

relative_position_bias_table 这个参数,构造的时候是 B

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented Apr 20, 2022

看了下 rank 0 的 INFO,在最开始,System-Src-WaitAndSendIds_1157 有两个 before,但是只有一个 after

这个是正常现象,因为整个集群都还在第1个 micro-batch 内,没运行到第2个 micro-batch,所以第2个 System-Src-WaitAndSendIds_1157 在等待用户触发。

@chengtbf
Copy link

用这个新的 commit 跑一下 :

91eade4

@Ldpe2G

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 28, 2022

用这个新的 commit 跑一下 :

91eade4

@Ldpe2G

报错消失了,但是还是卡住

@chengtbf
Copy link

用这个新的 commit 跑一下 :
91eade4
@Ldpe2G

报错消失了,但是还是卡住

日志上传一下, @leaves-zwx 文骁再分析一下死锁原因

@Ldpe2G
Copy link
Collaborator Author

Ldpe2G commented Apr 28, 2022

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented May 7, 2022

image

最新卡住的原因如上图,一句话概括,nccl logical op 的延迟创建 communicator 机制与 forward/backward 的不确定性执行顺序相互作用发生了死锁。

注:上述情况中的 ncclCommInitRank 的 ncclCudaMemcpy 没有修改,仍然保持原来的 cudaMemcpy 调用,也就是会发生 device 级别 sync。

比较奇怪的是修改了 nccl 的代码后把 ncclCommInitRank 中的 ncclCudaMemcpy 改成了非同步式(copy 发生在单独的 stream 上,并且阻塞至 copy 结束才继续往下执行)后,程序可以越过 nccl 1211 继续往下执行一段,但最终仍然还是死锁在某处,看起来像是 nccl 调用还是有某种死锁冲突,需要进一步的 debug 确认死锁原因。

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented May 13, 2022

  • 修改了 nccl 源码不用 default stream 而是用 temp stream 创建 communicator 仍然死锁,继续 debug 追究一下原因
  • 在 runtime init 阶段用类似 AddPlan 的方式创建所有的 nccl communicator(保证都在 main 线程创建,也可以避免因为线程销毁导致的 communicator 失效问题。最小补丁,其他基本都不动)

@leaves-zwx
Copy link
Collaborator

修改了 nccl 源码不用 default stream 而是用 temp stream 创建 communicator 仍然死锁,继续 debug 追究一下原因

目前初步 debug 出的原因是反向子图的 System-Boxing-BoxingCopy-1899 被放置在 forward stream 上,因为前向已被阻塞,所以 System-Boxing-BoxingCopy-1899 也无法执行。

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented May 13, 2022

补充一下 slice boxing copy 卡住的图例

image

  • rank0 forward thread: nccl 1199 执行不了,与其成对的 rank1 forward 上的 nccl 1199 无法执行
  • rank0 backward thread: slice boxing copy 1899 执行不了,因为其时序在 nccl 1199 之后
  • rank1 forward thread: nccl 1199 forward 之前的某个 forward op 执行不下去,因为其依赖的后向无法归还 regst
  • rank1 backward thread: nccl 1214 执行不下去,因为与其成对的 rank0 backward 上的 nccl 1214 无法执行

死锁的原理与前面 backward buffer op 被放到 forward stream 造成死锁的原因高度相似。#232 (comment)

@liujuncheng
Copy link
Contributor

补充一下 slice boxing copy 卡住的图例

image
  • rank0 forward thread: nccl 1199 执行不了,与其成对的 rank1 forward 上的 nccl 1199 无法执行
  • rank0 backward thread: slice boxing copy 1899 执行不了,因为其时序在 nccl 1199 之后
  • rank1 forward thread: nccl 1199 forward 之前的某个 forward op 执行不下去,因为其依赖的后向无法归还 regst
  • rank1 backward thread: nccl 1214 执行不下去,因为与其成对的 rank0 backward 上的 nccl 1214 无法执行

死锁的原理与前面 backward buffer op 被放到 forward stream 造成死锁的原因高度相似。#232 (comment)

https://github.com/Oneflow-Inc/oneflow/blob/master/oneflow/core/job_rewriter/insert_nccl_logical_op_pass.cpp#L749 是不是因为 nccl logical 不应该复用compute stream,这个判断去掉试试

@chengtbf
Copy link

可以,nccl logical 独立 compute 0 创建一个新的 stream,是可行的。

@yuanms2
Copy link

yuanms2 commented May 14, 2022

目前初步 debug 出的原因是反向子图的 System-Boxing-BoxingCopy-1899 被放置在 forward stream 上

这是个重要的发现,能找到源代码里是什么地方处理的吗?

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented May 15, 2022

可以,nccl logical 独立 compute 0 创建一个新的 stream,是可行的。

把 nccl logical 的前后向都独立出来,分别放到 nccl_compute_0 和 nccl_compute_1 stream 中去,原来的 default compute stream 中只剩下 tick, repeat, unpack, boxing copy/zeros/identity 等 op。

仍然死锁

image

  • rank 0 fwd thread: 卡在 4th forward 的 nccl 1199 处
  • rank 0 bwd thread: 卡在 1th backward 的最后的 conv2d filter grad cudnn 接口调用处
  • rank 1 fwd thread: 卡在 4th forward 的 gather_nd-33 处,其被 gather_nd-33_grad 阻塞,这个阻塞关系后面会解释
  • rank 1 bwd thread: 卡在 2th backward 的 nccl 1214 处

gather_nd-33_grad 为什么阻塞 gather_nd-33 的执行

2th backward 的 gather_nd-33_grad 阻塞 4th forward 的 gather_nd-33 的执行,主要是因为流水下的 regst 依赖关系,如下图所示:

image

从 repeat 有一条控制边连向 boxing zeros 1546,gather_nd-33_grad 消费 boxing zeros 1546,gather_nd-33_grad 如果不能执行,则 boxing zeros 1546 消费的 ctrl regst 不能还给 repeat,repeat 的 out regst 是有上限的,所以 gather_nd-33 可以提前执行但不能无限提前执行。

注意: repeat 到 boxing zeros 1546 的控制边在 rank0 上是不存在的,所以的可以看到 rank0 forward thread 中的 gather_nd-33 并没有被 rank0 backward thread 中的 gather_nd-33_grad 阻塞。

cudnn conv2d filter grad 为什么会阻塞?

其实被阻塞的 cuda 调用不止 cudnn conv2d filter grad,还有1个 copy H2D,还有 cudaStreamSynchronize (或 cudaEventSynchronize)。看起来像是 device sync 阻塞,但是翻遍所有的 thread stack,没有发现外部的 device sync 调用,只能猜测是内部某种同步机制被触发,特别是我们还修改了 nccl 某处逻辑。目前 libcuda 缺乏调试手段。

cudnn conv2d filter grad 栈
Thread 103 (Thread 0x7f5ad0ffd700 (LWP 497123)):
#0  0x00007f5d96b2218c in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#1  0x00007f5d96a9e7b7 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#2  0x00007f5d96ae4945 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#3  0x00007f5d96b15b0b in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#4  0x00007f5d96b1661b in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#5  0x00007f5d53324ed4 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#6  0x00007f5d53314a2e in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#7  0x00007f5d5332b664 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#8  0x00007f5d5332fdca in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#9  0x00007f5d53330044 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#10 0x00007f5d5332247f in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#11 0x00007f5d5330a5bd in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#12 0x00007f5d5335de31 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#13 0x00007f5d52a09154 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#14 0x00007f5d5297e7e7 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#15 0x00007f5d5977d341 in cudnn::cnn::EngineInterface::execute(cudnn::backend::VariantPack const&, CUstream_st*) () from /usr/local/cudnn/lib/libcudnn_cnn_infer.so.8
No symbol table info available.
#16 0x00007f5d52812095 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#17 0x00007f5d5977d341 in cudnn::cnn::EngineInterface::execute(cudnn::backend::VariantPack const&, CUstream_st*) () from /usr/local/cudnn/lib/libcudnn_cnn_infer.so.8
No symbol table info available.
#18 0x00007f5d52905cce in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#19 0x00007f5d52908802 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#20 0x00007f5d52915abe in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#21 0x00007f5d5977d341 in cudnn::cnn::EngineInterface::execute(cudnn::backend::VariantPack const&, CUstream_st*) () from /usr/local/cudnn/lib/libcudnn_cnn_infer.so.8
No symbol table info available.
#22 0x00007f5d59795411 in cudnn::backend::execute(cudnnContext*, cudnn::backend::ExecutionPlan&, cudnn::backend::VariantPack&) () from /usr/local/cudnn/lib/libcudnn_cnn_infer.so.8
No symbol table info available.
#23 0x00007f5d529734f9 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#24 0x00007f5d52972f62 in ?? () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#25 0x00007f5d52808973 in cudnnConvolutionBackwardFilter () from /usr/local/cudnn/lib/libcudnn_cnn_train.so.8
No symbol table info available.
#26 0x00007f5da205bc5c in oneflow::(anonymous namespace)::ConvFilterGradGpuKernel<float>::Compute (this=<optimized out>, ctx=0x7f5a215d0e30) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/device/cudnn_util.h:56
        _of_cudnn_check_status = <optimized out>
        dy = <optimized out>
        x = <optimized out>
        filter_diff = <optimized out>
        buf = <optimized out>
        cudnn_conf = <optimized out>
        args_and_algo = {args = {params = {static kTensorMaxDims = 5, static kConvMaxDims = 3, x_data_type = CUDNN_DATA_FLOAT, w_data_type = CUDNN_DATA_FLOAT, y_data_type = CUDNN_DATA_FLOAT, data_type = CUDNN_DATA_FLOAT, w_format = CUDNN_TENSOR_NCHW, x_ndim = 4, w_ndim = 4, y_ndim = 4, x_dims = {8, 3, 224, 224, 0}, x_strides = {150528, 50176, 224, 1, 0}, y_dims = {8, 96, 56, 56, 0}, y_strides = {301056, 3136, 56, 1, 0}, w_dims = {96, 3, 4, 4, 0}, padding = {0, 0, 0}, stride = {4, 4, 0}, dilation = {1, 1, 0}, max_ws_size = 1, groups = 1}, xdesc = {val_ = 0x7f5a222dde90}, ydesc = {val_ = 0x7f5a222e1470}, wdesc = {val_ = 0x7f5a222ddfe0}, cdesc = {val_ = 0x7f5a222e14e0}, heuristic = true, deterministic = false}, algo_perf = {algo = CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0, status = CUDNN_STATUS_SUCCESS, time = -1, memory = 0, determinism = CUDNN_NON_DETERMINISTIC, mathType = CUDNN_DEFAULT_MATH, reserved = {0, 1, 2}}}
        args = @0x7f5ad0ffc850: {params = {static kTensorMaxDims = 5, static kConvMaxDims = 3, x_data_type = CUDNN_DATA_FLOAT, w_data_type = CUDNN_DATA_FLOAT, y_data_type = CUDNN_DATA_FLOAT, data_type = CUDNN_DATA_FLOAT, w_format = CUDNN_TENSOR_NCHW, x_ndim = 4, w_ndim = 4, y_ndim = 4, x_dims = {8, 3, 224, 224, 0}, x_strides = {150528, 50176, 224, 1, 0}, y_dims = {8, 96, 56, 56, 0}, y_strides = {301056, 3136, 56, 1, 0}, w_dims = {96, 3, 4, 4, 0}, padding = {0, 0, 0}, stride = {4, 4, 0}, dilation = {1, 1, 0}, max_ws_size = 1, groups = 1}, xdesc = {val_ = 0x7f5a222dde90}, ydesc = {val_ = 0x7f5a222e1470}, wdesc = {val_ = 0x7f5a222ddfe0}, cdesc = {val_ = 0x7f5a222e14e0}, heuristic = true, deterministic = false}
        algo_perf = @0x7f5ad0ffc930: {algo = CUDNN_CONVOLUTION_BWD_FILTER_ALGO_0, status = CUDNN_STATUS_SUCCESS, time = -1, memory = 0, determinism = CUDNN_NON_DETERMINISTIC, mathType = CUDNN_DEFAULT_MATH, reserved = {0, 1, 2}}
#27 0x00007f5da0eb9112 in oneflow::user_op::OpKernel::Compute (ctx=<optimized out>, this=<optimized out>) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/kernel/user_kernel.cpp:642
CopyHD 栈
Thread 97 (Thread 0x7f5ada3b1700 (LWP 497079)):
#0  futex_abstimed_wait (private=<optimized out>, abstime=0x0, clockid=0, expected=3, futex_word=0x4978fcc) at ../sysdeps/nptl/futex-internal.h:284
        __ret = -512
        clockbit = 256
        op = <optimized out>
        __ret = <optimized out>
        err = <optimized out>
        err = <optimized out>
        __ret = <optimized out>
        clockbit = <optimized out>
        op = <optimized out>
        __ret = <optimized out>
        resultvar = <optimized out>
        __arg6 = <optimized out>
        __arg5 = <optimized out>
        __arg4 = <optimized out>
        __arg3 = <optimized out>
        __arg2 = <optimized out>
        __arg1 = <optimized out>
        _a6 = <optimized out>
        _a5 = <optimized out>
        _a4 = <optimized out>
        _a3 = <optimized out>
        _a2 = <optimized out>
        _a1 = <optimized out>
#1  __pthread_rwlock_wrlock_full (abstime=0x0, clockid=0, rwlock=0x4978fc0) at pthread_rwlock_common.c:731
        private = <optimized out>
        wf = <optimized out>
        err = <optimized out>
        prefer_writer = false
        may_share_futex_used_flag = true
        wpf = <optimized out>
        ready = <optimized out>
        r = <optimized out>
        may_share_futex_used_flag = <optimized out>
        r = <optimized out>
        wpf = <optimized out>
        ready = <optimized out>
        __value = <optimized out>
        prefer_writer = <optimized out>
        private = <optimized out>
        wf = <optimized out>
        err = <optimized out>
        w = <optimized out>
        w = <optimized out>
        private = <optimized out>
        err = <optimized out>
        w = <optimized out>
        wf = <optimized out>
        wf = <optimized out>
        __value = <optimized out>
#2  __GI___pthread_rwlock_wrlock (rwlock=0x4978fc0) at pthread_rwlock_wrlock.c:27
        result = <optimized out>
#3  0x00007f5d96d127b2 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#4  0x00007f5d96a8856f in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#5  0x00007f5d96a8a842 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#6  0x00007f5d96b36d5e in cuMemcpyAsync () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#7  0x00007f5da4e92e18 in __cudart602 () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#8  0x00007f5da4e6257d in __cudart739 () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#9  0x00007f5da4eb80b5 in cudaMemcpyAsync () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#10 0x00007f5da051a489 in oneflow::ep::primitive::(anonymous namespace)::MemcpyImpl::Launch (this=0x7f5a301aa620, count=1204224, src=0x7f58c2a49280, dst=0x7f58c26afa00, stream=<optimized out>) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/ep/cuda/primitive/memcpy.cpp:38
        _of_cuda_check_status = <optimized out>
        cuda_stream = <optimized out>
        cuda_stream = <optimized out>
        _of_cuda_check_status = <optimized out>
#11 oneflow::ep::primitive::(anonymous namespace)::MemcpyImpl::Launch (this=this@entry=0x7f5a301aa3c0, stream=<optimized out>, dst=dst@entry=0x7f58c26afa00, src=src@entry=0x7f58c2a49280, count=count@entry=1204224) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/ep/cuda/primitive/memcpy.cpp:35
        cuda_stream = <optimized out>
        _of_cuda_check_status = <optimized out>
#12 0x00007f5da0e6eed2 in oneflow::CopyHdKernel::ForwardDataContent (this=<optimized out>, ctx=0x7f5a301a83f8) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/register/blob_desc.h:55
        in_blob = <optimized out>
        out_blob = 0x7f5a301aa620
        body_byte_size = 1204224
cudaStreamSynchronize 栈
Thread 93 (Thread 0x7f5c20ffd700 (LWP 497025)):
#0  0x00007f5d96c3729e in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#1  0x00007f5d96ab8def in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#2  0x00007f5d96a9e435 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#3  0x00007f5d96d10a96 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#4  0x00007f5d96d11581 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#5  0x00007f5d96b22197 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#6  0x00007f5d96b04af5 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#7  0x00007f5d96cf9d3b in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#8  0x00007f5d96b342bb in cuStreamSynchronize () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#9  0x00007f5da4e5eac0 in __cudart1060 () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#10 0x00007f5da4ebab28 in cudaStreamSynchronize () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#11 0x00007f5d9f9b9698 in oneflow::ep::CudaStream::Sync (this=<optimized out>) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/ep/cuda/cuda_stream.cpp:147
        err = 553634272
#12 0x00007f5da0eaf735 in oneflow::SyncCheckKernelObserver::<lambda(char const*)>::operator() (_just_closure_func_name_=0x7f5da5321e98 "DidForwardDataContent", __closure=<optimized out>, __closure=<optimized out>) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/kernel/sync_check_kernel_observer.cpp:23
        _just_value_to_check_ = <optimized out>
        kernel_ctx = <optimized out>
        kernel = <optimized out>
        kernel_ctx = <optimized out>
        kernel = <optimized out>
        _just_value_to_check_ = <optimized out>
#13 oneflow::SyncCheckKernelObserver::DidForwardDataContent (this=<optimized out>, kernel_ctx=<optimized out>, kernel=0x7f5a69e52530) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/kernel/sync_check_kernel_observer.cpp:23
        __FUNCTION__ = "DidForwardDataContent"
#14 0x00007f5da0e6a9bb in oneflow::ChainKernelObserver::DidForwardDataContent (this=<optimized out>, kernel_ctx=0x7f5a69e455e8, kernel=0x7f5a69e52530) at /usr/include/c++/9/bits/shared_ptr_base.h:1020
        observer = std::shared_ptr<oneflow::KernelObserver> (use count 1, weak count 0) = {get() = 0x4c5e3b0}
        __for_range = <optimized out>
        __for_begin = <optimized out>
        __for_end = <optimized out>
如果不开启 kernel sync,cudaEventSynchronize 栈
Thread 95 (Thread 0x7f3e54ffd700 (LWP 501113)):
#0  0x00007f401a36e238 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#1  0x00007f401a1efdef in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#2  0x00007f401a1d5435 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#3  0x00007f401a447a96 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#4  0x00007f401a448581 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#5  0x00007f401a259197 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#6  0x00007f401a1cbb35 in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#7  0x00007f401a19185b in ?? () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#8  0x00007f401a28fd97 in cuEventSynchronize () from /lib/x86_64-linux-gnu/libcuda.so.1
No symbol table info available.
#9  0x00007f40285962ae in __cudart974 () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#10 0x00007f40285d3dd0 in cudaEventSynchronize () from /dataset/zwx_home/shared/tmp/oneflow/build/liboneflow.so
No symbol table info available.
#11 0x00007f40230eea8a in oneflow::ep::CudaEvent::Sync (this=<optimized out>) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/ep/cuda/cuda_event.cpp:42
        err = <optimized out>
#12 0x00007f402546fb7d in oneflow::(anonymous namespace)::CudaStreamContext::<lambda()>::operator() (__closure=0x392be7c8) at /dataset/zwx_home/shared/tmp/oneflow/oneflow/core/stream/cuda/cuda_stream_context.cpp:82
        cb_event = {first = 0x7f3a606f2ff0, second = {<std::_Maybe_unary_or_binary_function<void>> = {<No data fields>}, <std::_Function_base> = {static _M_max_size = 16, static _M_max_align = 8, _M_functor = {_M_unused = {_M_object = 0x7f3c39f82360, _M_const_object = 0x7f3c39f82360, _M_function_pointer = 0x7f3c39f82360, _M_member_pointer = (void (std::_Undefined_class::*)(std::_Undefined_class * const)) 0x7f3c39f82360, this adjustment 139913465032253}, _M_pod_data = "`#\370\071<\177\000\000=\356a$@\177\000"}, _M_manager = 0x7f4024630470 <std::_Function_base::_Base_manager<oneflow::(anonymous namespace)::LightActor<exec_kernel, inplace, IndexType, RegstIndex, StateContainer>::ActOnce() [with int exec_kernel = 1; int inplace = 0; IndexType = signed char; RegstIndex = oneflow::(anonymous namespace)::ArrayBaseIndex<signed char, 2>; StateContainer = oneflow::(anonymous namespace)::ArrayBaseStateContainer<signed char, 2>]::<lambda()> >::_M_manager(std::_Any_data &, const std::_Any_data &, std::_Manager_operation)>}, _M_invoker = 0x7f4024631ad0 <std::_Function_handler<void(), oneflow::(anonymous namespace)::LightActor<exec_kernel, inplace, IndexType, RegstIndex, StateContainer>::ActOnce() [with int exec_kernel = 1; int inplace = 0; IndexType = signed char; RegstIndex = oneflow::(anonymous namespace)::ArrayBaseIndex<signed char, 2>; StateContainer = oneflow::(anonymous namespace)::ArrayBaseStateContainer<signed char, 2>]::<lambda()> >::_M_invoke(const std::_Any_data &)>}}
        this = <optimized out>
        this = <optimized out>
        cb_event = <optimized out>

TODO

目前 libcuda 没有进一步调试手段,无法继续查证为什么 cudnn conv2d filter grad,copy hd 等卡住。有可能是我们修改了 nccl 源码的原因,目前先把 nccl 还原,然后修改 nccl commnunicator init 的逻辑解决 commnunicator init 的死锁,然后再测试看会不会死锁。

或者先把 cudnn conv2d filter grad 注释掉,使 conv2d filter grad kernel 空跑,看看还会不会出现该问题。

LOG

oneflow.INFO

job

plan

all thread backtrace

@yuanms2
Copy link

yuanms2 commented May 15, 2022

不同的线程上 op 的顺序不同?

其实这个就是pathways 为什么说gang scheduling的原因

@leaves-zwx
Copy link
Collaborator

不同的线程上 op 的顺序不同?

不同进程上的 op 顺序不同是存在的,有时也确实造成死锁,但上面这个死锁不是这个原因

forward thread: gather_nd-33 -> nccl_1199
backward thread: conv2d-1-FilterGrad -> identity-404_grad -> nccl_1214 -> gather_nd-33_grad

这几个 op 的序在 rank0 和 rank1 上是一致的。

但 rank1 上存在 1 条 repeat --> boxing_zeros -> gather_nd-33_grad 的控制边,实际上形成了 gather_nd-33_grad ---> gather_nd-33 这样的间接依赖关系(不在同一个 iter 内),但 rank0 上没有这样的依赖。

其实这个就是pathways 为什么说gang scheduling的原因

gang scheduling 能使很多问题变简单,当初实现 pipeline parallel 时也考虑过 gang scheduling 的方案,但当时选择了修改最小时间最短的方案。这里有最近一次的讨论:https://github.com/Oneflow-Inc/OneTeam/issues/1238#issuecomment-1095016837

@yuanms2
Copy link

yuanms2 commented May 15, 2022

为什么有的rank上有控制边,有的没有,不同rank上的子图不一样

@leaves-zwx
Copy link
Collaborator

为什么有的rank上有控制边,有的没有,不同rank上的子图不一样

B -> P 这样的 boxing,在本 rank 上有1个 boxing identity 的操作,其他 rank 上都是 boxing zeros 的操作,到 boxing identity 的边是 data regst ,到 boxing zeros 的边是 ctrl regst。

@leaves-zwx
Copy link
Collaborator

其实这个就是pathways 为什么说gang scheduling的原因

如果要实现 forward/backward 共用 stream,应该是需要实现 gang scheduling

@leaves-zwx
Copy link
Collaborator

修改了 repeat 的 ctrl out regst 的 num 后,NCCL_LAUNCH_MODE=PARALLEL 下可以运行通过,但在 NCCL_LAUNCH_MODE=GROUP 下还是会死锁。需要:

  • debug 死锁原因
  • 确认是否由于修改 nccl 源码引起(实现统一初始化的 nccl communicator 避免修改 nccl)

@leaves-zwx
Copy link
Collaborator

leaves-zwx commented May 16, 2022

Swin-T 3D 并行(2x2x2)死锁问题当前阶段总结

debug 过程中发现以下问题:

  • 1. backward source buffer op 被放置在 forward stream thread 上执行导致死锁
  • 2. nccl communicator init 带来 device sync 导致的死锁
  • 3. backward 过程中的 slice boxing copy 被放置在 forward stream thread 上导致死锁
  • 4. repeat ctrl out regst 没有像普通 out regst 一样 num 被设置成 repeat_num
  • 5. NCCL_LAUNCH_MODE=GROUP 时仍然死锁

Oneflow-Inc/oneflow#8226 目前做了2处修改,然后测试不再死锁。

其中第1点修改(nccl_compute_stream 独立)可以解决上面的问题1和3。在这2个问题中,某些原本属于 backward 的 op 被错误的放置在 forward stream 中,这是因为在 nccl logical ops pass 中,stage0 (在这个case中,rank0和rank1都属于stage0) 被分为2个 nccl_compute_stream,基本认为可以对应着 forward stream 和 backward stream。但当时实现时选择让 nccl_compute_stream_0 (也即 forward stream) 复用 default_compute_stream (不开启 nccl_use_compute_stream 时或无 pipeline parallel 时一般只有这个 default_compute_stream)。所以一般认为有2个主要 compute stream 在工作(忽略 copy stream 和 independent stream 等)。但由于某些原因,有些 op 在被添加时,它被加入到 default_compute_stream 中,但它的前后依赖 op 都在 backward stream 上,这就会导致 default_compute_stream (即 forward stream) 上插入一些与其他 op 没有依赖关系的 op,既然没有依赖关系,那插入的位置就只看物理时间点,所以有时在不同rank上插入位置会有很大的差异。这个修改就是让 nccl_compute_stream 不再复用 default_compute_stream,那么就变成3个主要的 compute stream 在工作,分别是 default_compute_stream, nccl_compute_stream_0, nccl_compute_stream_1,其中后2者充当着 forward stream 和 backward stream 的角色,而 default_compute_stream 中只留下了 tick, repeat, unpack, buffer, boxing copy/zeros/identity 等 op。这样修改后,可以避免1个与其他stream上op有依赖关系op插入到与其没有任何关联的stream上。

第2点修改(repeat ctrl out regst num 修复)可以解决上面的第4点问题,这个修改可以使后向不再阻塞前向(会导致不同 rank 的执行节奏差的比较远)。

为什么只改一部分问题就不死锁了?死锁的本质原因是什么?我们从一个简单的示意图看起:

image

图中的 D 如果起到屏障作用(比如有同步之类的操作)那么它在 rank0 和 rank1 上不同的插入的位置就会引起死锁问题(A, B, C 都是需要同步的操作,例如 nccl)。

上面的问题1、2、3点实质上都是起到了这个屏障作用。比如 nccl communicator init 带来的 device sync,放置错误的 backward ops 会使依赖其的 backward nccl op 带来屏障。屏障的特点是其与其他在同一个 thread 中执行的算子没有依赖关系。

而问题4则是屏障在rank0插入在 B 之后,rank1插入在B之前的原因。gather_nd-33_grad 消费的 variable,在 acc > 1 时消费的实际是 repeat;如果此时 grad 与 variable 的 sbp 不一致,且是 B -> P 这种情况,那么还需要插入 slice_boxing;这个 slice_boxing 会带来不平衡的子图,其在 rank0 上插入的是 boxing identity,有数据边直接连向 repeat,而在其他 rank 上插入的是 boxing zeros,只有控制边连向 repeat,而控制边的 regst num 是 1,数据边的 regst num 是 4(acc_steps == 4),那么我们就可以想见 rank0 上的某轮前向的某些 op (比如 B) 可以比其他 rank 上的同一轮的前向的同样 op 更早执行,因为其他 rank 上的 B 还要等后向归还那个 regst_num 只有1的控制边。所以这就造成了上面这种示意图的情况,rank0 上屏障在 B 之后,rank1 上屏障在 B 之前。

为什么问题2不改就可以解除死锁?

实际上经过测试只改掉问题4 (repeat ctrl out regst num) 测试就不再发生死锁了,因为改掉 4 后,屏障分别插在 B 之前和 B 之后的情形就消失了,即使有产生屏障的问题(比如 2)也不会带来死锁。

是 swin-T 的特殊网络结构和问题4共同作用带来的死锁。这也是为什么之前测试的 bert gpt 没有死锁,而到 swin-T 这里频繁发生的原因。(其实还跟目前 swin-T 错误的 sbp 配置有关系,这样才会出现奇怪的 slice boxing)

swin-T 与 gpt 结构上的1个重大差别是:在每一层 transformer layer 的 activation 与 variable 计算前有1个 gather 操作,例如 variable -> gather -> matmul。注意这个 gather 与 source op (variable) 非常接近,所以哪怕是非常靠后的 transformer layer 的 gather 也有可能早于非常靠前的比如 embeding 中的 conv2d 操作。这比较违反直觉,但这是 oneflow 执行的一种特点,即执行时完全不考虑算子的 python 序

swin-T 在执行时,前向刚开始没多久就需要执行大量的 gather 操作先,且这些 gather 每次执行是顺序不确定的,同时由于 repeat ctrl out regst num 带来的其他 rank 上(除 rank0)的 gather 反向会阻塞 gather 前向的问题,使 rank0 和 rank1 上的某轮前向执行物理时间点上差别比较大,才导致屏障们有可乘之机插入到不同位置。问题4的修复使 rank0 和 rank1 上的执行步调变得基本一致,屏障们很难再插入到不同的关键点位置。

但很难不代表不可能,如果有新的不常见的模型结构,或者只是不同 rank 的执行波动导致了执行的步调不一致,屏障们仍然有可能插在不同关键点位置导致死锁。所以消除屏障仍然是必须的。修复 repeat ctrl out regst num 问题只是降低了死锁发生的概率。如果有 gang scheduling 可能能完全避免死锁。

所以问题2仍然需要解决

对于问题5的猜测:NCCL_LAUNCH_MODE=GROUP 在 single-client 下有用,其保证不同 device 上的 nccl 执行顺序保持一致。但在 multi-client 下可能是负作用,因为1个rank只有1个device,GROUP 只带来了更加严格的执行顺序要求,使原本在 PARALLEL 模式下稍微宽松一点的可以执行下去的偏序在 GROUP 下执行不了。

但如果没有特别紧急的其他事情,还是有必要调试一下看 GROUP 下到底是什么执行序导致的死锁。一是增加对 nccl 执行方式的更深的理解;二是排除可能存在的潜在 bug。

@chengtbf 的修改 Oneflow-Inc/oneflow#8087 可以不是必要的?其想把 backward buffer op 放置到单独的 stream 里面去,但是否只要不跟 forward op 在1个 stream 就行了?nccl compute stream 独立那个改法也能达到同样目的。

@yuanms2
Copy link

yuanms2 commented May 17, 2022

其中第1点修改(nccl compute stream 独立)可以解决上面的第1和3点问题,因为这2个问题中被错误放置的 op 现在被留在了 default compute stream 中了,原来复用 default compute stream 的 forward ops 被放到 nccl compute stream 0 中去执行了,原来的 backward 由 nccl compute stream 0 变到 nccl compute stream 1 中执行。

这段话不是那么清晰,可以refine下

@leaves-zwx
Copy link
Collaborator

将 forward nccl_compute_stream 从 default_compute_stream 中分离出来。这样在 last stage 或者不开启 pipeline parallel 情况下(即 forward 和 backward 是同1个 nccl_compute_stream),对内存共享算法有冲击吗?毕竟之前是所有op都在1个stream 内,现在分在nccl_compute_stream和default_compute_stream 2个stream内,会导致chain断裂,内存复用结果不佳吗?

@chengtbf
Copy link

将 forward nccl_compute_stream 从 default_compute_stream 中分离出来。这样在 last stage 或者不开启 pipeline parallel 情况下(即 forward 和 backward 是同1个 nccl_compute_stream),对内存共享算法有冲击吗?毕竟之前是所有op都在1个stream 内,现在分在nccl_compute_stream和default_compute_stream 2个stream内,会导致chain断裂,内存复用结果不佳吗?

不会。因为 nccl compute stream 里的 subgraph 就是一个 chain。合并不到一个 subgraph(同一个 nccl stream) 里的,本就不在一个 chain 里。

@leaves-zwx
Copy link
Collaborator

如果把所有 comm init 的过程挪到 Runtime 初始化阶段,会使 ncclCommInitRank 的调用方式有死锁风险。runtime 初始化是在 main 线程完成的,与 vm worker 线程中的 eager nccl 调用的先后顺序不是确定的。这样就可能发生下图的执行时序,不同的 ncclCommInitRank 调用时序会导致死锁。

image

@lixinqi
Copy link

lixinqi commented May 19, 2022

如果main在调用nccl初始化代码之前做vm sync呢?

@leaves-zwx
Copy link
Collaborator

如果main在调用nccl初始化代码之前做vm sync呢?

由于 graph 统一初始化 nccl comm 不是指令,就算在做这个之前调用 vm sync 也不能完全规避上面提的情况吧,只能说降低概率。除非说我 vm sync 后有1种办法把 vm 锁住,然后等待我 graph 的 nccl comm 统一初始化完成后,再解锁 vm 才能完全保证安全吧?

@lixinqi
Copy link

lixinqi commented May 19, 2022

vm sync做完之后,vm就没有任务了呀。

@leaves-zwx
Copy link
Collaborator

vm sync做完之后,vm就没有任务了呀。

明白了,现在 main 线程正在准备做 graph 的 nccl comm 统一初始化这个时间点上,也不可能再插入其他指令了。

@lixinqi
Copy link

lixinqi commented May 19, 2022

vm sync做完之后,vm就没有任务了呀。

明白了,现在 main 线程正在准备做 graph 的 nccl comm 统一初始化这个时间点上,也不可能再插入其他指令了。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
7 participants