/* * Copyright (C) 2017 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "MarkingConstraintSet.h" #include "MarkingConstraintSolver.h" #include "Options.h" #include "SimpleMarkingConstraint.h" #include "SuperSampler.h" #include namespace JSC { MarkingConstraintSet::MarkingConstraintSet(Heap& heap) : m_heap(heap) { } MarkingConstraintSet::~MarkingConstraintSet() { } void MarkingConstraintSet::didStartMarking() { m_unexecutedRoots.clearAll(); m_unexecutedOutgrowths.clearAll(); for (auto& constraint : m_set) { constraint->resetStats(); switch (constraint->volatility()) { case ConstraintVolatility::GreyedByExecution: m_unexecutedRoots.set(constraint->index()); break; case ConstraintVolatility::GreyedByMarking: m_unexecutedOutgrowths.set(constraint->index()); break; case ConstraintVolatility::SeldomGreyed: break; } } m_iteration = 1; } void MarkingConstraintSet::add(CString abbreviatedName, CString name, ::Function function, ConstraintVolatility volatility, ConstraintConcurrency concurrency, ConstraintParallelism parallelism) { add(makeUnique(WTFMove(abbreviatedName), WTFMove(name), WTFMove(function), volatility, concurrency, parallelism)); } void MarkingConstraintSet::add( std::unique_ptr constraint) { constraint->m_index = m_set.size(); m_ordered.append(constraint.get()); if (constraint->volatility() == ConstraintVolatility::GreyedByMarking) m_outgrowths.append(constraint.get()); m_set.append(WTFMove(constraint)); } bool MarkingConstraintSet::executeConvergence(SlotVisitor& visitor) { bool result = executeConvergenceImpl(visitor); dataLogIf(Options::logGC(), " "); return result; } bool MarkingConstraintSet::isWavefrontAdvancing(SlotVisitor& visitor) { for (MarkingConstraint* outgrowth : m_outgrowths) { if (outgrowth->workEstimate(visitor)) return true; } return false; } bool MarkingConstraintSet::executeConvergenceImpl(SlotVisitor& visitor) { SuperSamplerScope superSamplerScope(false); MarkingConstraintSolver solver(*this); unsigned iteration = m_iteration++; dataLogIf(Options::logGC(), "i#", iteration, ":"); if (iteration == 1) { // First iteration is before any visitor draining, so it's unlikely to trigger any constraints // other than roots. solver.drain(m_unexecutedRoots); return false; } if (iteration == 2) { solver.drain(m_unexecutedOutgrowths); return false; } // We want to keep preferring the outgrowth constraints - the ones that need to be fixpointed // even in a stop-the-world GC - until they stop producing. They have a tendency to go totally // silent at some point during GC, at which point it makes sense not to run them again until // the end. Outgrowths producing new information corresponds almost exactly to the wavefront // advancing: it usually means that we are marking objects that should be marked based on // other objects that we would have marked anyway. Once the wavefront is no longer advancing, // we want to run mostly the root constraints (based on their predictions of how much work // they will have) because at this point we are just trying to outpace the retreating // wavefront. // // Note that this function (executeConvergenceImpl) only returns true if it runs all // constraints. So, all we are controlling are the heuristics that say which constraints to // run first. Choosing the constraints that are the most likely to produce means running fewer // constraints before returning. bool isWavefrontAdvancing = this->isWavefrontAdvancing(visitor); std::sort( m_ordered.begin(), m_ordered.end(), [&] (MarkingConstraint* a, MarkingConstraint* b) -> bool { // Remember: return true if a should come before b. auto volatilityScore = [] (MarkingConstraint* constraint) -> unsigned { return constraint->volatility() == ConstraintVolatility::GreyedByMarking ? 1 : 0; }; unsigned aVolatilityScore = volatilityScore(a); unsigned bVolatilityScore = volatilityScore(b); if (aVolatilityScore != bVolatilityScore) { if (isWavefrontAdvancing) return aVolatilityScore > bVolatilityScore; else return aVolatilityScore < bVolatilityScore; } double aWorkEstimate = a->workEstimate(visitor); double bWorkEstimate = b->workEstimate(visitor); if (aWorkEstimate != bWorkEstimate) return aWorkEstimate > bWorkEstimate; // This causes us to use SeldomGreyed vs GreyedByExecution as a final tie-breaker. return a->volatility() > b->volatility(); }); solver.converge(m_ordered); // Return true if we've converged. That happens if we did not visit anything. return !solver.didVisitSomething(); } void MarkingConstraintSet::executeAll(SlotVisitor& visitor) { for (auto& constraint : m_set) constraint->execute(visitor); dataLogIf(Options::logGC(), " "); } } // namespace JSC