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

Feat/logical nccl send recv #8318

Merged
merged 21 commits into from
Jun 1, 2022
Merged

Feat/logical nccl send recv #8318

merged 21 commits into from
Jun 1, 2022

Conversation

strint
Copy link
Contributor

@strint strint commented May 26, 2022

Insert send recv in nccl logical pass to enable memory reuse for special SBP.

@@ -366,7 +386,7 @@ bool TryBuildNcclLogicalOpConf(OperatorConf* ret, const OpNode* src_node, const
std::shared_ptr<Shape> dst_reduced_hierarchy = dst_reduced_parallel_desc->hierarchy();

if ((*src_reduced_hierarchy) == (*dst_reduced_hierarchy)
&& src_reduced_nd_sbp == dst_reduced_nd_sbp) {
&& (*src_reduced_nd_sbp) == (*dst_reduced_nd_sbp)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fix ndsbp equal

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 BUG 之前居然没遇到 😂

if (!got_nccl && ParseBooleanFromEnv("LOGICAL_SR", false)) {
got_nccl = TryBuildNcclBy2DHierarchyOthers(ret, *src_reduced_nd_sbp, *dst_reduced_nd_sbp,
src_reduced_hierarchy, lbn, scope_symbol_id,
logical_blob_desc);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Insert nccl logical send recv

// Go through all the ranks while transfer between two nd sbps with no PartialSum under the same
// placement.
// NOTE: We need to make sure no partial sums in the sbps of the producer and consumer.
void DfsTraverseRanks4NdSbp(
Copy link
Contributor Author

@strint strint May 27, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Borrowed from #7936 by @guo-ran

@strint strint requested a review from daquexian as a code owner May 27, 2022 09:14
eager_out = x.to_global(sbp=dst_nd_sbp, placement=placement)
test_case.assertTrue(np.array_equal(eager_out.numpy(), x.numpy()))

# bad case of graph: S with P
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bad cases

// Note: when src_nd_sbp has partial_sum, need a out_size buffer to copy and add to out.
buf_count += out_shape->elem_cnt();
}
return buf_count;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里错了,这里应该乘GetSizeOfDataType(data_type)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed. 现在单测和集成测试都正常了。

@@ -493,6 +496,20 @@ Maybe<void> BoxingCollector::AskSbpCombination(const NdSbp& sbp_producer, const
if (ParseBooleanFromEnv("ONEFLOW_BOXING_DISABLE_MIDDLE_NODE_AND_CHECK", false)) {
return Maybe<void>::Ok();
}
// If compute_cost==false + 2D sbp + same placment + nccl logical + not (p->b),
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这种条件下,middle node 不工作,交给 nccl logical send recv

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src 中有 p, dst 中 有 b时,不使用 send recv 而使用 middle node,发现 send recv 测试中存在无法通过的测试例子,在send recv测试中关闭 nccl use compute stream 就能复现问题。

所以这里 (p->b) 还是使用 send recv。存在的问题,后面的 PR 来修复。

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里解释一下存在的问题,2个:

  1. 传输速度由O(n+m)变为O(nm),n是上游节点P的维度分量乘积,m是下游节点B的维度分量乘积。
    例如: [2, 2]: (P, S0) -> [2, 2]: (B, B), O(2+4) -> O(2*4), 卡数少的时候差不了多少。
  2. 最后的B在各卡上数据不一致,会有1e-15的相对误差。

@@ -5329,6 +5329,24 @@ def OneFlow__ncclLogicalS2sOp : OneFlow_BaseOp<"_nccl_logical_s2s", [NoSideEffec
let has_nd_sbp_infer_fn = 1;
}

def OneFlow__ncclLogicalSendRecvOp : OneFlow_BaseOp<"_nccl_logical_send_recv", [NoSideEffect, NoGrad, DeclareOpInterfaceMethods<UserOpCompatibleInterface>]> {
Copy link
Contributor Author

@strint strint May 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nccl logical send recv op

bool AlwaysComputeWhenAllOutputsEmpty() const override { return false; }
};

void NcclLogicalSendRecv::Compute(user_op::KernelComputeContext* ctx, user_op::OpKernelState* state,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nccl logical send recv kernel


# check eager boxing
eager_out = x.to_global(sbp=dst_nd_sbp, placement=placement)
test_case.assertTrue(np.array_equal(eager_out.numpy(), x.numpy()))
Copy link
Contributor Author

@strint strint May 31, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

确保 eager 是正确的,因为后面 graph 的输出会调用 eager numpy(),里面隐式的调用了 dst_nd_sbp -> B

test_case.assertTrue(np.array_equal(eager_out.numpy(), x.numpy()))

# check graph boxing
flow.boxing.nccl.enable_use_compute_stream(True)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

打开 nccl logical 开关

#if flow.env.get_rank() == 0:
# print("src sbp ", src_nd_sbp, ", dst sbp ", dst_nd_sbp)

test_case.assertTrue(np.array_equal(out_np, in_np))
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

graph check

@github-actions
Copy link
Contributor

Code got formatted by CI. Please request CI again if you still want to have this PR merged. If the PR is from a forked repo, please download the patch files from the GitHub Actions web page and apply them locally.

@oneflow-ci-bot oneflow-ci-bot requested review from oneflow-ci-bot and removed request for oneflow-ci-bot May 31, 2022 15:51
@github-actions
Copy link
Contributor

Speed stats:

@github-actions
Copy link
Contributor

Static analysis with clang failed. PR label automerge has been removed

@strint strint requested review from oneflow-ci-bot and removed request for oneflow-ci-bot May 31, 2022 18:17
@github-actions
Copy link
Contributor

Code got formatted by CI. Please request CI again if you still want to have this PR merged. If the PR is from a forked repo, please download the patch files from the GitHub Actions web page and apply them locally.

@oneflow-ci-bot oneflow-ci-bot requested review from oneflow-ci-bot and removed request for oneflow-ci-bot May 31, 2022 19:21
@github-actions
Copy link
Contributor

View latest API docs preview at: https://staging.oneflow.info/docs/Oneflow-Inc/oneflow/pr/8318/

@github-actions
Copy link
Contributor

CI failed when running job: cuda-module. PR label automerge has been removed

@github-actions
Copy link
Contributor

Speed stats:

@strint
Copy link
Contributor Author

strint commented Jun 1, 2022

CI failed when running job: cuda-module. PR label automerge has been removed

https://github.com/Oneflow-Inc/oneflow/runs/6678073040?check_suite_focus=true

test_case = <test_module.TestModule testMethod=test_module_setattr>

    @flow.unittest.skip_unless_1n1d()
    def test_module_setattr(test_case):
        class CustomModule(flow.nn.Module):
            def __init__(self, param1, param2):
                super().__init__()
                self.param1 = param1
                self.param2 = param2
    
        param0 = flow.nn.Parameter(flow.Tensor(2, 3))
        param1 = flow.nn.Parameter(flow.Tensor(2, 3))
        param2 = CustomModule(param0, param1)
        m = CustomModule(param1, param2)
        params = list(m.parameters())
        test_case.assertEqual(len(params), 2)
    
        test_case.assertTrue(
            np.allclose(params[0].numpy(), param1.numpy(), atol=1e-4, rtol=1e-4)
        )
        test_case.assertTrue(
>           np.allclose(params[1].numpy(), param0.numpy(), atol=1e-4, rtol=1e-4)
        )
E       AssertionError: False is not true

一个无关的单测,本地没有复现。先重跑一下。

@strint strint requested review from oneflow-ci-bot and removed request for oneflow-ci-bot June 1, 2022 02:33
@github-actions
Copy link
Contributor

github-actions bot commented Jun 1, 2022

Speed stats:
GPU Name: NVIDIA GeForce GTX 1080 

❌ OneFlow resnet50 time: 130.5ms (= 13050.8ms / 100, input_shape=[16, 3, 224, 224])
PyTorch resnet50 time: 147.0ms (= 14700.5ms / 100, input_shape=[16, 3, 224, 224])
✔️ Relative speed: 1.13 (= 147.0ms / 130.5ms)

OneFlow resnet50 time: 76.9ms (= 7692.9ms / 100, input_shape=[8, 3, 224, 224])
PyTorch resnet50 time: 86.8ms (= 8684.3ms / 100, input_shape=[8, 3, 224, 224])
✔️ Relative speed: 1.13 (= 86.8ms / 76.9ms)

OneFlow resnet50 time: 54.7ms (= 10931.0ms / 200, input_shape=[4, 3, 224, 224])
PyTorch resnet50 time: 56.7ms (= 11335.9ms / 200, input_shape=[4, 3, 224, 224])
✔️ Relative speed: 1.04 (= 56.7ms / 54.7ms)

OneFlow resnet50 time: 41.2ms (= 8244.8ms / 200, input_shape=[2, 3, 224, 224])
PyTorch resnet50 time: 43.6ms (= 8713.9ms / 200, input_shape=[2, 3, 224, 224])
✔️ Relative speed: 1.06 (= 43.6ms / 41.2ms)

OneFlow resnet50 time: 37.8ms (= 7560.0ms / 200, input_shape=[1, 3, 224, 224])
PyTorch resnet50 time: 37.3ms (= 7458.3ms / 200, input_shape=[1, 3, 224, 224])
✔️ Relative speed: 0.99 (= 37.3ms / 37.8ms)

OneFlow swin dataloader time: 0.242s (= 48.458s / 200, num_workers=1)
PyTorch swin dataloader time: 0.152s (= 30.375s / 200, num_workers=1)
Relative speed: 0.627 (= 0.152s / 0.242s)

OneFlow swin dataloader time: 0.067s (= 13.369s / 200, num_workers=4)
PyTorch swin dataloader time: 0.042s (= 8.393s / 200, num_workers=4)
Relative speed: 0.628 (= 0.042s / 0.067s)

OneFlow swin dataloader time: 0.035s (= 7.040s / 200, num_workers=8)
PyTorch swin dataloader time: 0.022s (= 4.481s / 200, num_workers=8)
Relative speed: 0.636 (= 0.022s / 0.035s)

❌ OneFlow resnet50 time: 146.4ms (= 14640.5ms / 100, input_shape=[16, 3, 224, 224], ddp, world size=2)
PyTorch resnet50 time: 173.4ms (= 17342.0ms / 100, input_shape=[16, 3, 224, 224], ddp, world size=2)
✔️ Relative speed: 1.18 (= 173.4ms / 146.4ms)

OneFlow resnet50 time: 96.9ms (= 9690.9ms / 100, input_shape=[8, 3, 224, 224], ddp, world size=2)
PyTorch resnet50 time: 112.5ms (= 11250.2ms / 100, input_shape=[8, 3, 224, 224], ddp, world size=2)
✔️ Relative speed: 1.16 (= 112.5ms / 96.9ms)

OneFlow resnet50 time: 71.1ms (= 14230.0ms / 200, input_shape=[4, 3, 224, 224], ddp, world size=2)
PyTorch resnet50 time: 88.6ms (= 17712.6ms / 200, input_shape=[4, 3, 224, 224], ddp, world size=2)
✔️ Relative speed: 1.24 (= 88.6ms / 71.1ms)

OneFlow resnet50 time: 60.4ms (= 12088.9ms / 200, input_shape=[2, 3, 224, 224], ddp, world size=2)
PyTorch resnet50 time: 74.6ms (= 14920.4ms / 200, input_shape=[2, 3, 224, 224], ddp, world size=2)
✔️ Relative speed: 1.23 (= 74.6ms / 60.4ms)

OneFlow resnet50 time: 54.4ms (= 10886.4ms / 200, input_shape=[1, 3, 224, 224], ddp, world size=2)
PyTorch resnet50 time: 71.5ms (= 14296.9ms / 200, input_shape=[1, 3, 224, 224], ddp, world size=2)
✔️ Relative speed: 1.31 (= 71.5ms / 54.4ms)

@mergify mergify bot merged commit 0e42dca into master Jun 1, 2022
@mergify mergify bot deleted the feat/logical_nccl_send_recv branch June 1, 2022 03:38
bool has_independent_stream_;
std::string stream_name_;
std::unique_ptr<ParallelDesc> parallel_desc_;
mutable std::unique_ptr<Comm> comm_;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为啥是 mutable ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ncclComm_t comm() const { return GetOrCreateComm().comm; }

印象中好像是这样的 const 接口,会改动 comm_

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

那其实是不是就不需要作为 const 接口呢? 😂

参考其他的 nccl logical kernel:

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

是的,看了下,可以去掉 const

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants