Skip to content

Conversation

@pritamdamania87
Copy link
Contributor

@pritamdamania87 pritamdamania87 commented Sep 23, 2020

Stack from ghstack:

init_process_group and new_group update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: D23858025

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Sep 23, 2020
`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

ghstack-source-id: 112656257
Pull Request resolved: #45181
Copy link
Contributor

@mrshenli mrshenli left a comment

Choose a reason for hiding this comment

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

Stamp to unblock.

# barrier at the end to ensure that once we return from this method, all
# process groups including global variables are updated correctly on all
# ranks.
barrier()
Copy link
Contributor

Choose a reason for hiding this comment

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

We might need to guard this with try-except or if-else as it's possible that 3rd party extensions for c10d does not support barrier. See #45126

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm wondering how 3rd party extensions would work without supporting barrier? Currently ProcessGroup.hpp clearly defines the barrier method as pure virtual and as a result we should probably require extensions to support this. Do you know if there are any existing 3rd party extensions that do not support this? It looks like oneCCL and torch-ucc do support barrier().

Copy link
Contributor

Choose a reason for hiding this comment

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

IIRC, we have one test c10d extension whose barrier() implementation just throws an unsupported error.

@dr-ci
Copy link

dr-ci bot commented Sep 23, 2020

💊 CI failures summary and remediations

As of commit 408ede9 (more details on the Dr. CI page):


💚 💚 Looks good so far! There are no failures yet. 💚 💚


This comment was automatically generated by Dr. CI (expand for details).Follow this link to opt-out of these comments for your Pull Requests.

Please report bugs/suggestions on the GitHub issue tracker or post in the (internal) Dr. CI Users group.

See how this bot performed.

This comment has been revised 21 times.

@lw
Copy link
Contributor

lw commented Sep 23, 2020

How did you check that this fixes those failures? The OSX job on CircleCI is failing (for another reason) and from its logs it seems it's not running the DdpUnderDistAutogradTest suite.

@pritamdamania87
Copy link
Contributor Author

How did you check that this fixes those failures? The OSX job on CircleCI is failing (for another reason) and from its logs it seems it's not running the DdpUnderDistAutogradTest suite.

Shen pointed me to this: https://circleci.com/docs/2.0/ssh-access-jobs/. Basically you re-run an existing CircleCI job with ssh enabled. So I sshed into the vm, and ran the test 100 times and some runs failed. Then I locally applied the fix in this PR and ran the test 100 times again and all runs passed.

…up."

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Sep 23, 2020
Pull Request resolved: #45181

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378
ghstack-source-id: 112735141

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)
# barrier at the end to ensure that once we return from this method, all
# process groups including global variables are updated correctly on all
# ranks.
barrier()
Copy link
Contributor

Choose a reason for hiding this comment

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

cc @agolynski FYI, after this change, c10d extensions cannot throw in barrier(), otherwise they won't be able to initialize default process groups.

…up."

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Sep 24, 2020
Pull Request resolved: #45181

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378
ghstack-source-id: 112851804

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)
@codecov
Copy link

codecov bot commented Sep 25, 2020

Codecov Report

Merging #45181 into gh/pritamdamania87/164/base will decrease coverage by 0.24%.
The diff coverage is 16.66%.

Impacted file tree graph

@@                       Coverage Diff                       @@
##           gh/pritamdamania87/164/base   #45181      +/-   ##
===============================================================
- Coverage                        68.08%   67.84%   -0.25%     
===============================================================
  Files                              393      384       -9     
  Lines                            50960    50051     -909     
===============================================================
- Hits                             34697    33955     -742     
+ Misses                           16263    16096     -167     
Impacted Files Coverage Δ
.../testing/_internal/distributed/distributed_test.py 30.82% <0.00%> (-0.04%) ⬇️
torch/distributed/distributed_c10d.py 28.34% <50.00%> (+1.01%) ⬆️
torch/nn/modules/distance.py 64.00% <0.00%> (-20.00%) ⬇️
torch/utils/_benchmark/utils/common.py 77.68% <0.00%> (-13.23%) ⬇️
torch/testing/_internal/common_cuda.py 54.21% <0.00%> (-9.83%) ⬇️
torch/backends/cuda/__init__.py 62.50% <0.00%> (-8.34%) ⬇️
torch/nn/modules/loss.py 93.97% <0.00%> (-3.78%) ⬇️
torch/quantization/__init__.py 86.66% <0.00%> (-0.84%) ⬇️
torch/quantization/quantize.py 90.27% <0.00%> (-0.82%) ⬇️
torch/quantization/fx/quantize.py 96.79% <0.00%> (-0.46%) ⬇️
... and 43 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bee1d44...408ede9. Read the comment docs.

…up."

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Sep 25, 2020
Pull Request resolved: #45181

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378
ghstack-source-id: 112880857

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)
…up."

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Sep 25, 2020
Pull Request resolved: #45181

`init_process_group` and `new_group` update a bunch of global
variables after initializing the actual process group. As a result, there is a
race that after initializing the process group on say rank 0, if we immediately
check the default process group on rank 1 (say via RPC), we might actually get
an error since rank 1 hasn't yet updated its _default_pg variable.

To resolve this issue, I've added barrier() at the end of both of these calls.
This ensures that once these calls return we are guaranteed about correct
initialization on all ranks.

Since these calls are usually done mostly during initialization, it should be
fine to add the overhead of a barrier() here.

#Closes: #40434, #40378
ghstack-source-id: 112923112

Differential Revision: [D23858025](https://our.internmc.facebook.com/intern/diff/D23858025/)
@facebook-github-bot
Copy link
Contributor

This pull request has been merged in a2b4177.

@facebook-github-bot facebook-github-bot deleted the gh/pritamdamania87/164/head branch September 29, 2020 14:23
pritamdamania87 pushed a commit that referenced this pull request Oct 1, 2020
Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.

Differential Revision: [D24038839](https://our.internmc.facebook.com/intern/diff/D24038839/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Oct 1, 2020
Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.

Differential Revision: [D24038839](https://our.internmc.facebook.com/intern/diff/D24038839/)

ghstack-source-id: 113302257
Pull Request resolved: #45642
pritamdamania87 pushed a commit that referenced this pull request Oct 2, 2020
Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.

Differential Revision: [D24038839](https://our.internmc.facebook.com/intern/diff/D24038839/)

[ghstack-poisoned]
pritamdamania87 pushed a commit that referenced this pull request Oct 2, 2020
Pull Request resolved: #45642

Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.
ghstack-source-id: 113490343

Differential Revision: [D24038839](https://our.internmc.facebook.com/intern/diff/D24038839/)
facebook-github-bot pushed a commit that referenced this pull request Oct 5, 2020
Summary:
Pull Request resolved: #45642

Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.
ghstack-source-id: 113490343

Test Plan: waitforbuildbot

Reviewed By: osalpekar

Differential Revision: D24038839

fbshipit-source-id: a1f1db52cabcfb83e06c1a11ae9744afbf03f8dc
pritamdamania87 pushed a commit that referenced this pull request Oct 9, 2020
)

Summary:

Note: This PR has been merged into master at b5a2f04 after the 1.7 branch cut
(see original PR: #45642). This PR is to merge it into the 1.7 branch.

---- Original Commit Description Follows ---

Pull Request resolved: #45642

Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.
ghstack-source-id: 113490343

Test Plan: waitforbuildbot

Reviewed By: osalpekar

Differential Revision: D24038839

fbshipit-source-id: a1f1db52cabcfb83e06c1a11ae9744afbf03f8dc
malfet pushed a commit that referenced this pull request Oct 12, 2020
) (#46073)

Summary:

Note: This PR has been merged into master at b5a2f04 after the 1.7 branch cut
(see original PR: #45642). This PR is to merge it into the 1.7 branch.

---- Original Commit Description Follows ---

Pull Request resolved: #45642

Prior to #45181, initializing a
NCCL process group would work even if no GPUs were present. Although, now since
init_process_group calls `barrier()` this would fail.

In general the problem was that we could initialize ProcessGroupNCCL without
GPUs and then if we called a method like `barrier()` the process would crash
since we do % numGPUs resulting in division by zero.
ghstack-source-id: 113490343

Test Plan: waitforbuildbot

Reviewed By: osalpekar

Differential Revision: D24038839

fbshipit-source-id: a1f1db52cabcfb83e06c1a11ae9744afbf03f8dc

Co-authored-by: Pritam Damania <[email protected]>
jaglinux added a commit to jaglinux/pytorch that referenced this pull request Nov 13, 2020
If world_size is lesser than or equal to number of GPU's available
then the rank can be directly mapped to corresponding GPU.
This fixes the issue referenced in pytorch#45435 and pytorch#47629

For world_size = 3 and number of GPU's = 8, the rank to GPU mapping
will be 0,2,4. This is due to the introduction of barrier,
(refer pytorch#45181)
the tensors in barrier is mapped to cuda0,1,2 and the tensors in the
actual test cases are mapped to cuda0,2,4 resulting in different streams and
leading to timeout. This issue is specific to default process group.
Issue is not observed in new process group since the streams are created again
after the initial barrier call.

This patch maps the rank to corresponding GPU's when the world_size is
less than or equal to the number of GPU's, in this case 0,1,2

Note: The barrier function in distributed_c10d.py should include new parameter
to specify the tensor or rank to GPU mapping. In that case, this patch will be
redundant but harmless since the tests can specify the tensors with appropriate
GPU rankings.
facebook-github-bot pushed a commit that referenced this pull request Nov 14, 2020
Summary:
If world_size is lesser than or equal to number of GPU's available
then the rank can be directly mapped to corresponding GPU.
This fixes the issue referenced in #45435 and #47629

For world_size = 3 and number of GPU's = 8, the rank to GPU mapping
will be 0,2,4. This is due to the introduction of barrier,
(refer PR #45181)
the tensors in barrier is mapped to cuda0,1,2 and the tensors in the
actual test cases are mapped to cuda0,2,4 resulting in different streams and
leading to timeout. This issue is specific to default process group.
Issue is not observed in new process group since the streams are created again
after the initial barrier call.

This patch maps the rank to corresponding GPU's when the world_size is
less than or equal to the number of GPU's, in this case 0,1,2

Note: The barrier function in distributed_c10d.py should include new parameter
to specify the tensor or rank to GPU mapping. In that case, this patch will be
redundant but harmless since the tests can specify the tensors with appropriate
GPU rankings.

Fixes #47629

Pull Request resolved: #47898

Reviewed By: smessmer

Differential Revision: D24956021

Pulled By: rohan-varma

fbshipit-source-id: a88257f22a7991ba36566329766c106d3360bb4e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants