Skip to content

Improves ATen CUDAEvent#11293

Closed
mruberry wants to merge 12 commits intopytorch:masterfrom
mruberry:cuda_event_improvement
Closed

Improves ATen CUDAEvent#11293
mruberry wants to merge 12 commits intopytorch:masterfrom
mruberry:cuda_event_improvement

Conversation

@mruberry
Copy link
Collaborator

@mruberry mruberry commented Sep 5, 2018

After submitting PR #9726, PR #10581 created a different CUDAEvent class. The CUDAEvent proposed in #9726 was similar to the c10d::CUDAEvent class with additional testing and functionality. In particular, it was movable but not copyable. The CUDAEvent created by #10581 is refcounted and copyable. This PR retains the refcounting of the latter PR while fixing several bugs, adding tests, and extending the functionality to support testing and usage like in PR #8354. In particular, this PR:

  • Adds set_device() to CUDAContext
  • Adds three CUDAEvent tests to stream_test.cpp
  • Fixes three bugs:
  • Refcounting was broken. Destroying an of the RAIIs holding a particular CUDAEvent would destroy the event UNLESS it was the last RAII (the check was backwards).
  • Moving an event would cause a segfault.
  • Events were not destroyed on the device they were created on. See PR add device to CUDAEvent #9415 (@pietern)
  • Adds the happened() and recordOnce() functions
  • Changes the record() functions to not be const
  • Adds additional assertions to verify correctness

This PR does not:

  • Make c10d use the ATen CUDAEvent (this is appropriate for a separate PR)

Whether events should be refcounted is an interesting question. It adds some atomic operations and makes event creation eager. Making events movable but not copyable (like the c10d events) avoids these costs and allows events to be lazily constructed. Lazy construction is preferable when working with containers (like std::array or std::vector) and because the event's device can be set automatically to the first stream it's recorded on. With eager construction the user is required to understand that events have a device and acquire the device of the stream the event will be recorded on upfront. This can be seen here:

// NB: CUDA binds the event to a device at creation time, so we can initialize it
// only now, when we know we're on the correct device.
state.event.emplace();

and that file is the only one which currently uses the ATen CUDAEvent.

Refcounting does allow single writer multi-reader scenarios, although these scenarios can be also be supported by providing indirect access to the underlying CUDAEvent. I believe all current and planned usage scenarios do not require refcounting, and if desired I can update this PR to remove refcounting and make the ATen event movable but not copyable like the c10d event. I think not refcounting is preferable because it can improve performance, ease usability, and simplify the code (as seen with two of the above bugs).

I have decided to separate this from PR #8354 since while it's required for PR #8354 the changes are, clearly, of independent interest. PR #8354 has a new dependency on this one, however. I am closing PR #9726 in favor of this PR.

@apaszke @ezyang @pietern

}

CUDAEvent& operator=(CUDAEvent other) noexcept {
CUDAEvent& operator=(CUDAEvent other) {

This comment was marked as off-topic.

@ezyang
Copy link
Contributor

ezyang commented Sep 5, 2018

Windows error is legit:

21:34:35 stream_test.cpp.obj : error LNK2019: unresolved external symbol "public: void __cdecl at::cuda::CUDAStream::synchronize_with(struct at::cuda::CUDAEvent const &)const " (?synchronize_with@CUDAStream@cuda@at@@QEBAXAEBUCUDAEvent@23@@Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____14(void)" (?____C_A_T_C_H____T_E_S_T____14@@YAXXZ)
21:34:35 stream_test.cpp.obj : error LNK2019: unresolved external symbol "public: void __cdecl at::cuda::CUDAEvent::record(struct at::cuda::CUDAStream const &)" (?record@CUDAEvent@cuda@at@@QEAAXAEBUCUDAStream@23@@Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____18(void)" (?____C_A_T_C_H____T_E_S_T____18@@YAXXZ)
21:34:35 stream_test.cpp.obj : error LNK2019: unresolved external symbol "public: void __cdecl at::cuda::CUDAEvent::recordOnce(struct at::cuda::CUDAStream const &)" (?recordOnce@CUDAEvent@cuda@at@@QEAAXAEBUCUDAStream@23@@Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____14(void)" (?____C_A_T_C_H____T_E_S_T____14@@YAXXZ)
21:34:35 bin\stream_test.exe : fatal error LNK1120: 3 unresolved externals

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

ezyang has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@mruberry
Copy link
Collaborator Author

mruberry commented Sep 5, 2018

Tests look clear except for pr/caffe2-py2-cuda9.0-cudnn7-windows-build which appears to be broken atm. We may want to wait for that to be fixed since we were hitting Windows-specific errors there earlier.

Removing refcounting is causing a new error I need to diagnose tomorrow.

@pietern
Copy link
Contributor

pietern commented Sep 6, 2018

@mruberry Nice work. I think it will be straightforward to update c10d to use this implementation. But I think we should wait with that until the stream PR is merged as well.

@mruberry
Copy link
Collaborator Author

mruberry commented Sep 6, 2018

@pietern Which stream PR?

@pietern
Copy link
Contributor

pietern commented Sep 6, 2018

@mruberry Ah nevermind, I read #8354 as being the stream PR but the actual stream PR has already been merged.

@mruberry
Copy link
Collaborator Author

mruberry commented Sep 6, 2018

I think this PR is now ready. The remaining failure appears to be unrelated (and happening with other PRs, too?).

20:22:19 FAIL: test_scalar_fusion (main.TestScript)
20:22:19 ----------------------------------------------------------------------
20:22:19 Traceback (most recent call last):
20:22:19 File "test_jit.py", line 2961, in test_scalar_fusion
20:22:19 self.assertExpectedGraph(ge.graph_for(x, y))
20:22:19 File "test_jit.py", line 248, in assertExpectedGraph
20:22:19 self.assertExpected(str(graph), *args, **kwargs)
20:22:19 File "/var/lib/jenkins/workspace/test/common.py", line 524, in assertExpected
20:22:19 self.assertMultiLineEqual(expected, s)
20:22:19 AssertionError: 'graph(%x : Float()\n %y : Float()) {\n %2 : Float() = prim::FusionGroup_0 [truncated]... != 'graph(%x : Float()\n %y : Float()) {\n %2 : Float() = aten::type_as(%y, % [truncated]...
20:22:19 graph(%x : Float()
20:22:19 %y : Float()) {
20:22:19 - %2 : Float() = prim::FusionGroup_0[device=-1](%x, %y)
20:22:19 - return (%2);
20:22:19 - }
20:22:19 - with prim::FusionGroup_0 = graph(%0 : Float()
20:22:19 - %1 : Float()) {
20:22:19 - %2 : Float() = aten::type_as(%1, %0)
20:22:19 ? ^ ^
20:22:19 + %2 : Float() = aten::type_as(%y, %x)
20:22:19 ? ^ ^
20:22:19 %3 : int = prim::Constantvalue=1
20:22:19 - %4 : Float() = aten::add(%0, %2, %3)
20:22:19 ? ^
20:22:19 + %4 : Float() = aten::add(%x, %2, %3)
20:22:19 ? ^
20:22:19 return (%4);
20:22:19 }
20:22:19
20:22:19
20:22:19 ----------------------------------------------------------------------
20:22:19 Ran 1179 tests in 30.373s
20:22:19
20:22:19 FAILED (failures=1, skipped=44, expected failures=3)
20:22:19 Traceback (most recent call last):
20:22:19 File "test/run_test.py", line 391, in
20:22:19 main()
20:22:19 File "test/run_test.py", line 383, in main
20:22:19 raise RuntimeError(message)
20:22:19 RuntimeError: test_jit failed!

Copy link
Contributor

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

soumith has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@soumith
Copy link
Collaborator

soumith commented Sep 7, 2018

waiting on internal contbuilds. will land after that's finished (~3 hours)

zdevito pushed a commit to zdevito/ATen that referenced this pull request Sep 7, 2018
Summary:
After submitting PR #9726, PR #10581 created a different CUDAEvent class. The CUDAEvent proposed in #9726 was similar to the c10d::CUDAEvent class with additional testing and functionality. In particular, it was movable but not copyable. The CUDAEvent created by #10581 is refcounted and copyable. This PR retains the refcounting of the latter PR while fixing several bugs, adding tests, and extending the functionality to support testing and usage like in PR #8354. In particular, this PR:

- Adds set_device() to CUDAContext
- Adds three CUDAEvent tests to stream_test.cpp
- Fixes three bugs:
- Refcounting was broken. Destroying an of the RAIIs holding a particular CUDAEvent would destroy the event UNLESS it was the last RAII (the check was backwards).
- Moving an event would cause a segfault.
- Events were not destroyed on the device they were created on. See PR #9415 (pietern)
- Adds the happened() and recordOnce() functions
- Changes the record() functions to not be const
- Adds additional assertions to verify correctness

This PR does not:

- Make c10d use the ATen CUDAEvent (this is appropriate for a separate PR)

Whether events should be refcounted is an interesting question. It adds some atomic operations and makes event creation eager. Making events movable but not copyable (like the c10d events) avoids these costs and allows events to be lazily constructed. Lazy construction is preferable when working with containers (like std::array or std::vector) and because the event's device can be set automatically to the first stream it's recorded on. With eager construction the user is required to understand that events have a device and acquire the device of the stream the event will be recorded on upfront. This can be seen here:

https://github.com/pytorch/pytorch/blob/542aadd9a7609892e207c1e15de08a975b697752/aten/src/ATen/native/cudnn/RNN.cpp#L1130-L1132

and that file is the only one which currently uses the ATen CUDAEvent.

Refcounting does allow single writer multi-reader scenarios, although these scenarios can be also be supported by providing indirect access to the underlying CUDAEvent. I believe all current and planned usage scenarios do not require refcounting, and if desired I can update this PR to remove refcounting and make the ATen event movable but not copyable like the c10d event. I think not refcounting is preferable because it can improve performance, ease usability, and simplify the code (as seen with two of the above bugs).

I have decided to separate this from PR #8354 since while it's required for PR #8354 the changes are, clearly, of independent interest. PR #8354 has a new dependency on this one, however. I am closing PR #9726 in favor of this PR.

apaszke ezyang pietern
Pull Request resolved: pytorch/pytorch#11293

Differential Revision: D9665836

Pulled By: soumith

fbshipit-source-id: a1513fa4f9761e2f304d126e402f6b6950e1c1d2
PenghuiCheng pushed a commit to PenghuiCheng/pytorch that referenced this pull request Sep 11, 2018
Summary:
After submitting PR pytorch#9726, PR pytorch#10581 created a different CUDAEvent class. The CUDAEvent proposed in pytorch#9726 was similar to the c10d::CUDAEvent class with additional testing and functionality. In particular, it was movable but not copyable. The CUDAEvent created by pytorch#10581 is refcounted and copyable. This PR retains the refcounting of the latter PR while fixing several bugs, adding tests, and extending the functionality to support testing and usage like in PR pytorch#8354. In particular, this PR:

- Adds set_device() to CUDAContext
- Adds three CUDAEvent tests to stream_test.cpp
- Fixes three bugs:
- Refcounting was broken. Destroying an of the RAIIs holding a particular CUDAEvent would destroy the event UNLESS it was the last RAII (the check was backwards).
- Moving an event would cause a segfault.
- Events were not destroyed on the device they were created on. See PR pytorch#9415 (pietern)
- Adds the happened() and recordOnce() functions
- Changes the record() functions to not be const
- Adds additional assertions to verify correctness

This PR does not:

- Make c10d use the ATen CUDAEvent (this is appropriate for a separate PR)

Whether events should be refcounted is an interesting question. It adds some atomic operations and makes event creation eager. Making events movable but not copyable (like the c10d events) avoids these costs and allows events to be lazily constructed. Lazy construction is preferable when working with containers (like std::array or std::vector) and because the event's device can be set automatically to the first stream it's recorded on. With eager construction the user is required to understand that events have a device and acquire the device of the stream the event will be recorded on upfront. This can be seen here:

https://github.com/pytorch/pytorch/blob/542aadd9a7609892e207c1e15de08a975b697752/aten/src/ATen/native/cudnn/RNN.cpp#L1130-L1132

and that file is the only one which currently uses the ATen CUDAEvent.

Refcounting does allow single writer multi-reader scenarios, although these scenarios can be also be supported by providing indirect access to the underlying CUDAEvent. I believe all current and planned usage scenarios do not require refcounting, and if desired I can update this PR to remove refcounting and make the ATen event movable but not copyable like the c10d event. I think not refcounting is preferable because it can improve performance, ease usability, and simplify the code (as seen with two of the above bugs).

I have decided to separate this from PR pytorch#8354 since while it's required for PR pytorch#8354 the changes are, clearly, of independent interest. PR pytorch#8354 has a new dependency on this one, however. I am closing PR pytorch#9726 in favor of this PR.

apaszke ezyang pietern
Pull Request resolved: pytorch#11293

Differential Revision: D9665836

Pulled By: soumith

fbshipit-source-id: a1513fa4f9761e2f304d126e402f6b6950e1c1d2
@mruberry mruberry deleted the cuda_event_improvement branch September 25, 2018 16:39
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.

5 participants