Executes a hierarchical tree broadcast. Each subdiv is a broadcast between a subset of the devices. If there is only one task, there is one subdiv comprising a broadcast between all devices belonging to the task. If there are n tasks, n>1, then there are n+1 subdivs. In the first (global) subdiv, one device from each task participates in a binary tree broadcast. Each task receives a copy of the t
| 290 | // corresponds to broadcast between all devices on task i. Thus, each task |
| 291 | // participates in at most 2 subdivs. |
| 292 | void HierarchicalTreeBroadcaster::RunTree() { |
| 293 | int num_subdivs = static_cast<int>(col_params_->subdiv_rank.size()); |
| 294 | // TODO(b/78352018): this is easily improved when a node participates in both |
| 295 | // first and second subdivision. It would first send to its descendents in |
| 296 | // the first subdiv, then wait until all pending ops are finished before |
| 297 | // sending to descendents in second subdiv. A better implementation would |
| 298 | // collapse the two send blocks. |
| 299 | for (int si = 0; si < num_subdivs; si++) { |
| 300 | int my_rank = col_params_->subdiv_rank[si]; |
| 301 | // If rank is -1, this device does not participate in this subdiv. |
| 302 | if (-1 == my_rank) continue; |
| 303 | int source_rank = col_params_->instance.impl_details.subdiv_source_rank[si]; |
| 304 | if (VLOG_IS_ON(1)) { |
| 305 | string subdiv_buf; |
| 306 | for (int r : col_params_->instance.impl_details.subdiv_permutations[si]) { |
| 307 | strings::StrAppend(&subdiv_buf, r, ","); |
| 308 | } |
| 309 | VLOG(1) << "Running Broadcast tree device=" << col_ctx_->device_name |
| 310 | << " subdiv=" << si << " perm=" << subdiv_buf |
| 311 | << " my_rank=" << my_rank << " source_rank=" << source_rank; |
| 312 | } |
| 313 | |
| 314 | mutex mu; // also guards status_ while callbacks are pending |
| 315 | int pending_count = 0; // GUARDED_BY(mu) |
| 316 | condition_variable all_done; |
| 317 | |
| 318 | if (my_rank >= 0 && my_rank != source_rank) { |
| 319 | // Begin by receiving the value. |
| 320 | profiler::TraceMe activity( |
| 321 | [&] { return strings::StrCat("ReceiveValue:", si); }, |
| 322 | profiler::TraceMeLevel::kInfo); |
| 323 | int recv_from_rank = TreeRecvFrom(*col_params_, si); |
| 324 | Notification note; |
| 325 | DispatchRecv(si, recv_from_rank, my_rank, col_ctx_->output, |
| 326 | [this, &mu, ¬e](const Status& s) { |
| 327 | mutex_lock l(mu); |
| 328 | status_.Update(s); |
| 329 | note.Notify(); |
| 330 | }); |
| 331 | note.WaitForNotification(); |
| 332 | } |
| 333 | |
| 334 | // Then forward value to all descendent devices. |
| 335 | { |
| 336 | profiler::TraceMe activity( |
| 337 | [&] { return strings::StrCat("ForwardValue:", si); }, |
| 338 | profiler::TraceMeLevel::kInfo); |
| 339 | if (my_rank >= 0 && status_.ok()) { |
| 340 | std::vector<int> send_to_ranks; |
| 341 | TreeSendTo(*col_params_, si, &send_to_ranks); |
| 342 | for (int i = 0; i < send_to_ranks.size(); ++i) { |
| 343 | int target_rank = send_to_ranks[i]; |
| 344 | { |
| 345 | mutex_lock l(mu); |
| 346 | ++pending_count; |
| 347 | } |
| 348 | DispatchSend(si, target_rank, my_rank, |
| 349 | (is_source_ ? col_ctx_->input : col_ctx_->output), |
nothing calls this directly
no test coverage detected