| 500 | } |
| 501 | |
| 502 | virtual at::Tensor reduceGrad(at::Tensor grad_tensor, long ds_id) |
| 503 | { |
| 504 | int world_size = process_group_->getSize(); |
| 505 | const DSParam& param = param_registry_->getParam(ds_id); |
| 506 | const auto expected_grad_dtype = param.getExpectedGradDtype(); |
| 507 | // Match PyTorch's leaf grad accumulation dtype before bucket selection: |
| 508 | // https://docs.pytorch.org/docs/main/generated/torch.sparse.semi_structured.SparseSemiStructuredTensorCUSPARSELT.html#torch.sparse.semi_structured.SparseSemiStructuredTensorCUSPARSELT.grad_dtype |
| 509 | if (expected_grad_dtype.has_value() && |
| 510 | grad_tensor.scalar_type() != expected_grad_dtype.value()) { |
| 511 | grad_tensor = grad_tensor.to(expected_grad_dtype.value()); |
| 512 | } |
| 513 | const auto scalar_type = grad_tensor.scalar_type(); |
| 514 | std::shared_ptr<ReduceBucket> reduce_bucket = reduce_buckets_->getBuffer(scalar_type); |
| 515 | |
| 516 | auto comp_stream = at::cuda::getCurrentCUDAStream(); |
| 517 | |
| 518 | if (reduce_bucket->shouldFlush(grad_tensor.numel())) { |
| 519 | int rank = process_group_->getRank(); |
| 520 | |
| 521 | flushReduceBucket(scalar_type); |
| 522 | |
| 523 | // reduce_bucket is swapped in flushReduceBucket if double buffering is enabled |
| 524 | reduce_bucket = reduce_buckets_->getBuffer(scalar_type); |
| 525 | } |
| 526 | |
| 527 | if (grad_tensor.numel() > reduce_bucket->getSize()) { |
| 528 | // extend buckets |
| 529 | at::cuda::stream_synchronize(rs_stream_); |
| 530 | reduce_bucket->reserve(grad_tensor.numel()); |
| 531 | } |
| 532 | |
| 533 | at::Tensor reduce_in_buffer = reduce_bucket->allocate(grad_tensor.numel()); |
| 534 | |
| 535 | // This ensures the order of reduce_scatter -> copy |
| 536 | // Without this block, copy may start while reduce_scatter is still running |
| 537 | reduce_buckets_->getEvent(scalar_type)->block(comp_stream); |
| 538 | auto copy_src = grad_tensor.contiguous().view({-1}).detach(); |
| 539 | // keep references to copy src |
| 540 | reduce_tasks_[scalar_type].emplace_back(ds_id, copy_src, reduce_in_buffer); |
| 541 | |
| 542 | // computation must be done before copy |
| 543 | rs_comp_done_events_[ds_id]->record(comp_stream); |
| 544 | rs_comp_done_events_[ds_id]->block(copy_stream_); |
| 545 | { |
| 546 | at::cuda::CUDAStreamGuard guard(copy_stream_); |
| 547 | reduce_in_buffer.copy_(copy_src, true); |
| 548 | rs_copy_done_events_[ds_id]->record(copy_stream_); |
| 549 | } |
| 550 | |
| 551 | return at::Tensor(); |
| 552 | } |
| 553 | |
| 554 | bool hasParam(long ds_id) const { return hasKey(has_acc_grad_, ds_id); } |
| 555 |
nothing calls this directly
no test coverage detected