void MarkStackArray::stealSomeCellsFrom(MarkStackArray& other, size_t idleThreadCount) { // Try to steal 1 / Nth of the shared array, where N is the number of idle threads. // To reduce copying costs, we prefer stealing a whole segment over stealing // individual cells, even if this skews away from our 1 / N target. ASSERT(m_segmentCapacity == other.m_segmentCapacity); validatePrevious(); other.validatePrevious(); // If other has an entire segment, steal it and return. if (other.m_topSegment->m_previous) { ASSERT(other.m_topSegment->m_previous->m_top == m_segmentCapacity); // First remove a segment from other. MarkStackSegment* current = other.m_topSegment->m_previous; other.m_topSegment->m_previous = current->m_previous; other.m_numberOfPreviousSegments--; ASSERT(!!other.m_numberOfPreviousSegments == !!other.m_topSegment->m_previous); // Now add it to this. current->m_previous = m_topSegment->m_previous; m_topSegment->m_previous = current; m_numberOfPreviousSegments++; validatePrevious(); other.validatePrevious(); return; } size_t numberOfCellsToSteal = (other.size() + idleThreadCount - 1) / idleThreadCount; // Round up to steal 1 / 1. while (numberOfCellsToSteal-- > 0 && other.canRemoveLast()) append(other.removeLast()); }
void MarkStackArray::stealSomeCellsFrom(MarkStackArray& other) { ASSERT(m_segmentCapacity == other.m_segmentCapacity); validatePrevious(); other.validatePrevious(); // If other has an entire segment, steal it and return. if (other.m_topSegment->m_previous) { ASSERT(other.m_topSegment->m_previous->m_top == m_segmentCapacity); // First remove a segment from other. MarkStackSegment* current = other.m_topSegment->m_previous; other.m_topSegment->m_previous = current->m_previous; other.m_numberOfPreviousSegments--; ASSERT(!!other.m_numberOfPreviousSegments == !!other.m_topSegment->m_previous); // Now add it to this. current->m_previous = m_topSegment->m_previous; m_topSegment->m_previous = current; m_numberOfPreviousSegments++; validatePrevious(); other.validatePrevious(); return; } // Otherwise drain 1/Nth of the shared array where N is the number of // workers, or Options::minimumNumberOfCellsToKeep, whichever is bigger. size_t numberOfCellsToSteal = std::max((size_t)Options::minimumNumberOfCellsToKeep, other.size() / Options::numberOfGCMarkers); while (numberOfCellsToSteal-- > 0 && other.canRemoveLast()) append(other.removeLast()); }
void MarkStackArray::stealSomeCellsFrom(MarkStackArray& other, size_t idleThreadCount) { // Try to steal 1 / Nth of the shared array, where N is the number of idle threads. // To reduce copying costs, we prefer stealing a whole segment over stealing // individual cells, even if this skews away from our 1 / N target. validatePrevious(); other.validatePrevious(); // If other has an entire segment, steal it and return. if (other.m_numberOfSegments > 1) { // Move the heads of the lists aside. We'll push them back on after. MarkStackSegment* otherHead = other.m_segments.removeHead(); MarkStackSegment* myHead = m_segments.removeHead(); ASSERT(other.m_segments.head()->m_top == s_segmentCapacity); m_segments.push(other.m_segments.removeHead()); m_numberOfSegments++; other.m_numberOfSegments--; m_segments.push(myHead); other.m_segments.push(otherHead); validatePrevious(); other.validatePrevious(); return; } size_t numberOfCellsToSteal = (other.size() + idleThreadCount - 1) / idleThreadCount; // Round up to steal 1 / 1. while (numberOfCellsToSteal-- > 0 && other.canRemoveLast()) append(other.removeLast()); }
void SlotVisitor::donateKnownParallel(MarkStackArray& from, MarkStackArray& to) { // NOTE: Because we re-try often, we can afford to be conservative, and // assume that donating is not profitable. // Avoid locking when a thread reaches a dead end in the object graph. if (from.size() < 2) return; // If there's already some shared work queued up, be conservative and assume // that donating more is not profitable. if (to.size()) return; // If we're contending on the lock, be conservative and assume that another // thread is already donating. std::unique_lock<Lock> lock(m_heap.m_markingMutex, std::try_to_lock); if (!lock.owns_lock()) return; // Otherwise, assume that a thread will go idle soon, and donate. from.donateSomeCellsTo(to); m_heap.m_markingConditionVariable.notifyAll(); }
void MarkStackArray::transferTo(MarkStackArray& other) { RELEASE_ASSERT(this != &other); // Remove our head and the head of the other list. GCArraySegment<const JSCell*>* myHead = m_segments.removeHead(); GCArraySegment<const JSCell*>* otherHead = other.m_segments.removeHead(); m_numberOfSegments--; other.m_numberOfSegments--; other.m_segments.append(m_segments); other.m_numberOfSegments += m_numberOfSegments; m_numberOfSegments = 0; // Put the original heads back in their places. m_segments.push(myHead); other.m_segments.push(otherHead); m_numberOfSegments++; other.m_numberOfSegments++; while (!isEmpty()) { refill(); while (canRemoveLast()) other.append(removeLast()); } }
bool MarkStackArray::donateSomeCellsTo(MarkStackArray& other) { ASSERT(m_segmentCapacity == other.m_segmentCapacity); validatePrevious(); other.validatePrevious(); // Fast check: see if the other mark stack already has enough segments. if (other.m_numberOfPreviousSegments + 1 >= Options::maximumNumberOfSharedSegments) return false; size_t numberOfCellsToKeep = Options::minimumNumberOfCellsToKeep; ASSERT(m_top > numberOfCellsToKeep || m_topSegment->m_previous); // Looks like we should donate! Give the other mark stack all of our // previous segments, and then top it off. MarkStackSegment* previous = m_topSegment->m_previous; while (previous) { ASSERT(m_numberOfPreviousSegments); MarkStackSegment* current = previous; previous = current->m_previous; current->m_previous = other.m_topSegment->m_previous; other.m_topSegment->m_previous = current; m_numberOfPreviousSegments--; other.m_numberOfPreviousSegments++; } ASSERT(!m_numberOfPreviousSegments); m_topSegment->m_previous = 0; validatePrevious(); other.validatePrevious(); // Now top off. We want to keep at a minimum numberOfCellsToKeep, but if // we really have a lot of work, we give up half. if (m_top > numberOfCellsToKeep * 2) numberOfCellsToKeep = m_top / 2; while (m_top > numberOfCellsToKeep) other.append(removeLast()); return true; }
void MarkStackArray::donateSomeCellsTo(MarkStackArray& other) { // Try to donate about 1 / 2 of our cells. To reduce copying costs, // we prefer donating whole segments over donating individual cells, // even if this skews away from our 1 / 2 target. size_t segmentsToDonate = m_numberOfSegments / 2; // If we only have one segment (our head) we don't donate any segments. if (!segmentsToDonate) { size_t cellsToDonate = m_top / 2; // Round down to donate 0 / 1 cells. while (cellsToDonate--) { ASSERT(m_top); other.append(removeLast()); } return; } validatePrevious(); other.validatePrevious(); // Remove our head and the head of the other list before we start moving segments around. // We'll add them back on once we're done donating. MarkStackSegment* myHead = m_segments.removeHead(); MarkStackSegment* otherHead = other.m_segments.removeHead(); while (segmentsToDonate--) { MarkStackSegment* current = m_segments.removeHead(); ASSERT(current); ASSERT(m_numberOfSegments > 1); other.m_segments.push(current); m_numberOfSegments--; other.m_numberOfSegments++; } // Put the original heads back in their places. m_segments.push(myHead); other.m_segments.push(otherHead); validatePrevious(); other.validatePrevious(); }
void MarkStackArray::donateSomeCellsTo(MarkStackArray& other) { // Try to donate about 1 / 2 of our cells. To reduce copying costs, // we prefer donating whole segments over donating individual cells, // even if this skews away from our 1 / 2 target. ASSERT(m_segmentCapacity == other.m_segmentCapacity); size_t segmentsToDonate = (m_numberOfPreviousSegments + 2 - 1) / 2; // Round up to donate 1 / 1 previous segments. if (!segmentsToDonate) { size_t cellsToDonate = m_top / 2; // Round down to donate 0 / 1 cells. while (cellsToDonate--) { ASSERT(m_top); other.append(removeLast()); } return; } validatePrevious(); other.validatePrevious(); MarkStackSegment* previous = m_topSegment->m_previous; while (segmentsToDonate--) { ASSERT(previous); ASSERT(m_numberOfPreviousSegments); MarkStackSegment* current = previous; previous = current->m_previous; current->m_previous = other.m_topSegment->m_previous; other.m_topSegment->m_previous = current; m_numberOfPreviousSegments--; other.m_numberOfPreviousSegments++; } m_topSegment->m_previous = previous; validatePrevious(); other.validatePrevious(); }
size_t MarkStackArray::transferTo(MarkStackArray& other, size_t limit) { size_t count = 0; while (count < limit && !isEmpty()) { refill(); while (count < limit && canRemoveLast()) { other.append(removeLast()); count++; } } RELEASE_ASSERT(count <= limit); return count; }