| 3124 | } |
| 3125 | |
| 3126 | bool TxGraphImpl::DoWork(uint64_t max_cost) noexcept |
| 3127 | { |
| 3128 | uint64_t cost_done{0}; |
| 3129 | // First linearize everything in NEEDS_RELINEARIZE to an acceptable level. If more budget |
| 3130 | // remains after that, try to make everything optimal. |
| 3131 | for (QualityLevel quality : {QualityLevel::NEEDS_FIX, QualityLevel::NEEDS_RELINEARIZE, QualityLevel::ACCEPTABLE}) { |
| 3132 | // First linearize staging, if it exists, then main. |
| 3133 | for (int level = GetTopLevel(); level >= 0; --level) { |
| 3134 | // Do not modify main if it has any observers. |
| 3135 | if (level == 0 && m_main_chunkindex_observers != 0) continue; |
| 3136 | ApplyDependencies(level); |
| 3137 | auto& clusterset = GetClusterSet(level); |
| 3138 | // Do not modify oversized levels. |
| 3139 | if (clusterset.m_oversized == true) continue; |
| 3140 | auto& queue = clusterset.m_clusters[int(quality)]; |
| 3141 | while (!queue.empty()) { |
| 3142 | if (cost_done >= max_cost) return false; |
| 3143 | // Randomize the order in which we process, so that if the first cluster somehow |
| 3144 | // needs more work than what max_cost allows, we don't keep spending it on the same |
| 3145 | // one. |
| 3146 | auto pos = m_rng.randrange<size_t>(queue.size()); |
| 3147 | auto cost_now = max_cost - cost_done; |
| 3148 | if (quality == QualityLevel::NEEDS_FIX || quality == QualityLevel::NEEDS_RELINEARIZE) { |
| 3149 | // If we're working with clusters that need relinearization still, only perform |
| 3150 | // up to m_acceptable_cost work. If they become ACCEPTABLE, and we still |
| 3151 | // have budget after all other clusters are ACCEPTABLE too, we'll spend the |
| 3152 | // remaining budget on trying to make them OPTIMAL. |
| 3153 | cost_now = std::min(cost_now, m_acceptable_cost); |
| 3154 | } |
| 3155 | auto [cost, improved] = queue[pos].get()->Relinearize(*this, level, cost_now); |
| 3156 | cost_done += cost; |
| 3157 | // If no improvement was made to the Cluster, it means we've essentially run out of |
| 3158 | // budget. Even though it may be the case that cost_done < max_cost still, the |
| 3159 | // linearizer decided there wasn't enough budget left to attempt anything with. |
| 3160 | // To avoid an infinite loop that keeps trying clusters with minuscule budgets, |
| 3161 | // stop here too. |
| 3162 | if (!improved) return false; |
| 3163 | } |
| 3164 | } |
| 3165 | } |
| 3166 | // All possible work has been performed, so we can return true. Note that this does *not* mean |
| 3167 | // that all clusters are optimally linearized now. It may be that there is nothing to do left |
| 3168 | // because all non-optimal clusters are in oversized and/or observer-bearing levels. |
| 3169 | return true; |
| 3170 | } |
| 3171 | |
| 3172 | void BlockBuilderImpl::Next() noexcept |
| 3173 | { |