test_ops.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #!/usr/bin/env python
  2. # Copyright 2023 The HuggingFace Team. All rights reserved.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import torch
  16. from accelerate import PartialState
  17. from accelerate.test_utils.testing import assert_exception
  18. from accelerate.utils.dataclasses import DistributedType
  19. from accelerate.utils.operations import (
  20. DistributedOperationException,
  21. broadcast,
  22. copy_tensor_to_devices,
  23. gather,
  24. gather_object,
  25. pad_across_processes,
  26. reduce,
  27. )
  28. def create_tensor(state):
  29. return (torch.arange(state.num_processes) + 1.0 + (state.num_processes * state.process_index)).to(state.device)
  30. def test_gather(state):
  31. tensor = create_tensor(state)
  32. gathered_tensor = gather(tensor)
  33. assert gathered_tensor.tolist() == list(range(1, state.num_processes**2 + 1))
  34. def test_gather_object(state):
  35. # Gather objects in TorchXLA is not supported.
  36. if state.distributed_type == DistributedType.XLA:
  37. return
  38. obj = [state.process_index]
  39. gathered_obj = gather_object(obj)
  40. assert len(gathered_obj) == state.num_processes, f"{gathered_obj}, {len(gathered_obj)} != {state.num_processes}"
  41. assert gathered_obj == list(range(state.num_processes)), f"{gathered_obj} != {list(range(state.num_processes))}"
  42. def test_gather_non_contiguous(state):
  43. # Skip this test because the 'is_contiguous' function of XLA tensor always returns True.
  44. if state.distributed_type == DistributedType.XLA:
  45. return
  46. # Create a non-contiguous tensor (enforce non-contiguity after device memory allocation)
  47. tensor = torch.arange(12, device=state.device).view(4, 3).t()
  48. assert not tensor.is_contiguous()
  49. # Shouldn't error out
  50. _ = gather(tensor)
  51. def test_broadcast(state):
  52. tensor = create_tensor(state)
  53. broadcasted_tensor = broadcast(tensor)
  54. assert broadcasted_tensor.shape == torch.Size([state.num_processes])
  55. assert broadcasted_tensor.tolist() == list(range(1, state.num_processes + 1))
  56. def test_pad_across_processes(state):
  57. # We need to pad the tensor with one more element if we are the main process
  58. # to ensure that we can pad
  59. if state.is_main_process:
  60. tensor = torch.arange(state.num_processes + 1).to(state.device)
  61. else:
  62. tensor = torch.arange(state.num_processes).to(state.device)
  63. padded_tensor = pad_across_processes(tensor)
  64. assert padded_tensor.shape == torch.Size([state.num_processes + 1])
  65. if not state.is_main_process:
  66. assert padded_tensor.tolist() == list(range(0, state.num_processes)) + [0]
  67. def test_reduce_sum(state):
  68. # For now runs on only two processes
  69. if state.num_processes != 2:
  70. return
  71. tensor = create_tensor(state)
  72. reduced_tensor = reduce(tensor, "sum")
  73. truth_tensor = torch.tensor([4.0, 6]).to(state.device)
  74. assert torch.allclose(reduced_tensor, truth_tensor), f"{reduced_tensor} != {truth_tensor}"
  75. def test_reduce_mean(state):
  76. # For now runs on only two processes
  77. if state.num_processes != 2:
  78. return
  79. tensor = create_tensor(state)
  80. reduced_tensor = reduce(tensor, "mean")
  81. truth_tensor = torch.tensor([2.0, 3]).to(state.device)
  82. assert torch.allclose(reduced_tensor, truth_tensor), f"{reduced_tensor} != {truth_tensor}"
  83. def test_op_checker(state):
  84. # Must be in a distributed state, and gathering is currently not supported in TorchXLA.
  85. if state.distributed_type in [DistributedType.NO, DistributedType.XLA]:
  86. return
  87. state.debug = True
  88. # `pad_across_processes`
  89. if state.process_index == 0:
  90. data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
  91. else:
  92. data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4, 5]]]).to(state.device)}
  93. with assert_exception(DistributedOperationException):
  94. pad_across_processes(data, dim=0)
  95. # `reduce`
  96. if state.process_index == 0:
  97. data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
  98. else:
  99. data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]]).to(state.device)}
  100. with assert_exception(DistributedOperationException):
  101. reduce(data)
  102. # `broadcast`
  103. if state.process_index == 0:
  104. data = {"tensor": torch.tensor([[0.0, 1, 2, 3, 4]]).to(state.device)}
  105. else:
  106. data = {"tensor": torch.tensor([[[0.0, 1, 2, 3, 4], [5, 6, 7, 8, 9]]]).to(state.device)}
  107. with assert_exception(DistributedOperationException):
  108. broadcast(data)
  109. state.debug = False
  110. def test_copy_tensor_to_devices(state):
  111. if state.distributed_type not in [DistributedType.MULTI_GPU, DistributedType.XLA]:
  112. return
  113. if state.is_main_process:
  114. tensor = torch.tensor([1, 2, 3], dtype=torch.int).to(state.device)
  115. else:
  116. tensor = None
  117. tensor = copy_tensor_to_devices(tensor)
  118. assert torch.allclose(tensor, torch.tensor([1, 2, 3], dtype=torch.int, device=state.device))
  119. def _mp_fn(index):
  120. # For xla_spawn (TPUs)
  121. main()
  122. def main():
  123. state = PartialState()
  124. state.print(f"State: {state}")
  125. state.print("testing gather")
  126. test_gather(state)
  127. state.print("testing gather_object")
  128. test_gather_object(state)
  129. state.print("testing gather non-contiguous")
  130. test_gather_non_contiguous(state)
  131. state.print("testing broadcast")
  132. test_broadcast(state)
  133. state.print("testing pad_across_processes")
  134. test_pad_across_processes(state)
  135. state.print("testing reduce_sum")
  136. test_reduce_sum(state)
  137. state.print("testing reduce_mean")
  138. test_reduce_mean(state)
  139. state.print("testing op_checker")
  140. test_op_checker(state)
  141. state.print("testing sending tensors across devices")
  142. test_copy_tensor_to_devices(state)
  143. state.destroy_process_group()
  144. if __name__ == "__main__":
  145. main()