From 2ede341d59b4ab259caf808dfa65c0ac380ba347 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 8 Mar 2022 10:57:52 +0200 Subject: Improve performance of update during match for multiple targets --- libbuild2/adhoc-rule-buildscript.cxx | 14 +++----- libbuild2/algorithm.cxx | 69 ++++++++++++++++++++++++++++++++++++ libbuild2/algorithm.hxx | 13 +++++++ libbuild2/cc/link-rule.cxx | 14 +++----- libbuild2/target-state.hxx | 4 ++- 5 files changed, 95 insertions(+), 19 deletions(-) diff --git a/libbuild2/adhoc-rule-buildscript.cxx b/libbuild2/adhoc-rule-buildscript.cxx index fa60556..25ef1b7 100644 --- a/libbuild2/adhoc-rule-buildscript.cxx +++ b/libbuild2/adhoc-rule-buildscript.cxx @@ -456,17 +456,13 @@ namespace build2 // If we have any update during match prerequisites, now is the time to // update them. // - // Note also that we ignore the result and whether it renders us out of - // date, leaving it to the common execute logic in perform_update_*(). + // Note that we ignore the result and whether it renders us out of date, + // leaving it to the common execute logic in perform_update_*(). + // + // Note also that update_during_match() spoils prerequisite_target::data. // if (a == perform_update_id) - { - for (const prerequisite_target& pt: pts) - { - if ((pt.include & 2) != 0) - update_during_match (trace, a, *pt.target); - } - } + update_during_match (trace, a, pts, 2 /* mask */); // See if this is not update or not on a file-based target. // diff --git a/libbuild2/algorithm.cxx b/libbuild2/algorithm.cxx index cbf0495..d72e95e 100644 --- a/libbuild2/algorithm.cxx +++ b/libbuild2/algorithm.cxx @@ -2119,6 +2119,75 @@ namespace build2 } } + bool + update_during_match (tracer& trace, + action a, + prerequisite_targets& pts, + uintptr_t mask) + { + // On the first pass detect and handle unchanged tragets. Note that we + // have to do it in a separate pass since we cannot call matched_state() + // once we've switched the phase. + // + context* ctx (nullptr); + + for (prerequisite_target& p: pts) + { + if ((p.include & mask) != 0) + { + if (p.target != nullptr) + { + const target& pt (*p.target); + + target_state os (pt.matched_state (a)); + + if (os != target_state::unchanged) + { + if (ctx == nullptr) + ctx = &pt.ctx; + + p.data = static_cast (os); + continue; + } + } + + p.data = 0; + } + } + + // If all unchanged, we are done. + // + if (ctx == nullptr) + return false; + + phase_switch ps (*ctx, run_phase::execute); + + bool r (false); + + for (prerequisite_target& p: pts) + { + if ((p.include & mask) != 0 && p.data != 0) + { + const target& pt (*p.target); + + target_state os (static_cast (p.data)); + target_state ns (execute_direct (a, pt)); + + if (ns != os && ns != target_state::unchanged) + { + l6 ([&]{trace << "updated " << pt + << "; old state " << os + << "; new state " << ns;}); + r = true; + } + + p.data = 0; + } + } + + return r; + } + static inline void blank_adhoc_member (const target*&) { diff --git a/libbuild2/algorithm.hxx b/libbuild2/algorithm.hxx index c30680a..788138b 100644 --- a/libbuild2/algorithm.hxx +++ b/libbuild2/algorithm.hxx @@ -609,6 +609,19 @@ namespace build2 action, const target&, timestamp = timestamp_unknown); + // As above, but update all the targets in prerequisite_targets that have + // the specified mask in prerequisite_target::include. Return true if any of + // them have changed. + // + // Note that this function spoils prerequisite_target::data (which is used + // for temporary storage). But it resets data to 0 once done. + // + LIBBUILD2_SYMEXPORT bool + update_during_match (tracer&, + action, + prerequisite_targets&, + uintptr_t mask); + // The default prerequisite execute implementation. Call execute_async() on // each non-ignored (non-NULL) prerequisite target in a loop and then wait // for their completion. Return target_state::changed if any of them were diff --git a/libbuild2/cc/link-rule.cxx b/libbuild2/cc/link-rule.cxx index c507155..b72e5a0 100644 --- a/libbuild2/cc/link-rule.cxx +++ b/libbuild2/cc/link-rule.cxx @@ -1226,17 +1226,13 @@ namespace build2 // example, object file matches may need the headers to be already // updated). // - // Note also that we ignore the result and whether it renders us out of - // date, leaving it to the common execute logic in perform_update(). + // Note that we ignore the result and whether it renders us out of date, + // leaving it to the common execute logic in perform_update(). + // + // Note also that update_during_match() spoils prerequisite_target::data. // if (update_match) - { - for (const prerequisite_target& pto: pts) - { - if ((pto.include & 2) != 0) - update_during_match (trace, a, *pto.target); - } - } + update_during_match (trace, a, pts, 2 /* mask */); // Check if we have any binful utility libraries. // diff --git a/libbuild2/target-state.hxx b/libbuild2/target-state.hxx index 3457b13..48d683d 100644 --- a/libbuild2/target-state.hxx +++ b/libbuild2/target-state.hxx @@ -18,9 +18,11 @@ namespace build2 // Note that postponed is "greater" than unchanged since it may result in // the changed state. // + // Note also that value 0 is available to indicate absent/invalid state. + // enum class target_state: uint8_t { - unknown, + unknown = 1, unchanged, postponed, busy, -- cgit v1.1