Assemble memory cost
| 737 | |
| 738 | // Assemble memory cost |
| 739 | void SbpNode::InitializeMemory(bool is_reusable, const HashMap<LogicalBlobId, int32_t>& lbi2id, |
| 740 | const std::vector<int32_t>& id2count, bool nccl_use_compute_stream) { |
| 741 | const auto& curr_operator = op_node_->op(); |
| 742 | // An edge should not be initialized twice |
| 743 | // During each initialization, we are computing sum(memory of consumer) - sum(memory of producer) |
| 744 | // This is why we need to pre-store memory of producer |
| 745 | HashMap<SbpEdge*, std::vector<int64_t>> sbp_edge2nd_sbp_sig2memory; |
| 746 | for (const auto& obn : curr_operator.output_bns()) { |
| 747 | const LogicalBlobId& lbi = curr_operator.BnInOp2Lbi(obn); |
| 748 | // Fixed memory or in the support of the reusable memory |
| 749 | if (!is_reusable || id2count.at(lbi2id.at(lbi)) > 0) { |
| 750 | // If not in support, memory_ would be empty. |
| 751 | in_memory_support_ = true; |
| 752 | memory_.resize(sbp_sig_list_.size(), 0); |
| 753 | const auto& logical_blob_desc = op_node_->LogicalBlobDesc4Lbi(lbi); |
| 754 | const auto& hierarchy = *CHECK_JUST(curr_operator.GetParallelDesc4BnInOp(obn))->hierarchy(); |
| 755 | // There are some operators with a fixed sbp for some blobs, such as conv. |
| 756 | // {in: S0, kernel: B, out: S0} |
| 757 | // {in: B, kernel: B, out: B} |
| 758 | // The blob kernel have the same sbp for different signatures. |
| 759 | // We pre-store the results for the same sbp while accessing the same blobs. |
| 760 | HashMap<NdSbp, int64_t> nd_sbp2memory; |
| 761 | SbpEdge* edge_contain_lbi = nullptr; |
| 762 | for (const auto& edge_out : edges_out_) { |
| 763 | if (edge_out->SearchLbi(lbi)) { edge_contain_lbi = edge_out; } |
| 764 | } |
| 765 | // There exist some lbi which does not have a consumer |
| 766 | // At this moment edge_contain_lbi == nullptr |
| 767 | auto& nd_sbp_sig2memory = sbp_edge2nd_sbp_sig2memory[edge_contain_lbi]; |
| 768 | nd_sbp_sig2memory.resize(sbp_sig_list_.size(), 0); |
| 769 | for (int32_t sbp_sig_id = 0; sbp_sig_id < sbp_sig_list_.size(); sbp_sig_id++) { |
| 770 | const NdSbp& nd_sbp = sbp_sig_list_[sbp_sig_id].bn_in_op2nd_sbp().at(obn); |
| 771 | auto it = nd_sbp2memory.find(nd_sbp); |
| 772 | if (it == nd_sbp2memory.end()) { |
| 773 | // This compute the memory at rank 0, the largest one. |
| 774 | // We could be faster if we just compute the average memory. |
| 775 | it = nd_sbp2memory |
| 776 | .insert({nd_sbp, MaxByteSize4BlobDescSbp(logical_blob_desc, nd_sbp, hierarchy)}) |
| 777 | .first; |
| 778 | } |
| 779 | memory_[sbp_sig_id] += it->second; |
| 780 | nd_sbp_sig2memory[sbp_sig_id] += it->second; |
| 781 | } |
| 782 | } |
| 783 | } |
| 784 | // Even after the correction in the memory of edges, the relative error still have 0.73%. |
| 785 | if (nccl_use_compute_stream && in_memory_support_ && is_reusable) { |
| 786 | for (const auto& pair : sbp_edge2nd_sbp_sig2memory) { |
| 787 | // Init memory for each out-going edge |
| 788 | pair.first->InitializeMemory(lbi2id, id2count, pair.second); |
| 789 | } |
| 790 | } |
| 791 | } |
| 792 | |
| 793 | // Reduce and set the wait time for op in the trunk |
| 794 | void SbpNode::SetTrunkWaitTime(double trunk_wait_time) { |
no test coverage detected