public void Trim(uint tickCount, int id, Utilities.MemoryPressure pressure, int bucketSize) { LockedStack[] stacks = _perCoreStacks; for (int i = 0; i < stacks.Length; i++) { stacks[i].Trim(tickCount, id, pressure, bucketSize); } }
public bool Trim() { int milliseconds = Environment.TickCount; Utilities.MemoryPressure pressure = Utilities.GetMemoryPressure(); ArrayPoolEventSource log = ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferTrimPoll(milliseconds, (int)pressure); } PerCoreLockedStacks?[] perCoreBuckets = _buckets; for (int i = 0; i < perCoreBuckets.Length; i++) { perCoreBuckets[i]?.Trim((uint)milliseconds, Id, pressure, Utilities.GetMaxSizeForBucket(i)); } if (pressure == Utilities.MemoryPressure.High) { // Under high pressure, release all thread locals if (log.IsEnabled()) { foreach (KeyValuePair <T[]?[], object?> tlsBuckets in _allTlsBuckets) { T[]?[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) { T[]? buffer = Interlocked.Exchange(ref buckets[i], null); if (buffer is not null) { // As we don't want to take a perf hit in the rent path it // is possible that a buffer could be rented as we "free" it. log.BufferTrimmed(buffer.GetHashCode(), buffer.Length, Id); } } } } else { foreach (KeyValuePair <T[]?[], object?> tlsBuckets in _allTlsBuckets) { Array.Clear(tlsBuckets.Key); } } } return(true); }
public void Trim(uint tickCount, int id, Utilities.MemoryPressure pressure, int bucketSize) { const uint StackTrimAfterMS = 60 * 1000; // Trim after 60 seconds for low/moderate pressure const uint StackHighTrimAfterMS = 10 * 1000; // Trim after 10 seconds for high pressure const uint StackRefreshMS = StackTrimAfterMS / 4; // Time bump after trimming (1/4 trim time) const int StackLowTrimCount = 1; // Trim one item when pressure is low const int StackMediumTrimCount = 2; // Trim two items when pressure is moderate const int StackHighTrimCount = MaxBuffersPerArraySizePerCore; // Trim all items when pressure is high const int StackLargeBucket = 16384; // If the bucket is larger than this we'll trim an extra when under high pressure const int StackModerateTypeSize = 16; // If T is larger than this we'll trim an extra when under high pressure const int StackLargeTypeSize = 32; // If T is larger than this we'll trim an extra (additional) when under high pressure if (_count == 0) { return; } uint trimTicks = pressure == Utilities.MemoryPressure.High ? StackHighTrimAfterMS : StackTrimAfterMS; lock (this) { if (_count > 0 && _firstStackItemMS > tickCount || (tickCount - _firstStackItemMS) > trimTicks) { // We've wrapped the tick count or elapsed enough time since the // first item went into the stack. Drop the top item so it can // be collected and make the stack look a little newer. ArrayPoolEventSource log = ArrayPoolEventSource.Log; int trimCount = StackLowTrimCount; switch (pressure) { case Utilities.MemoryPressure.High: trimCount = StackHighTrimCount; // When pressure is high, aggressively trim larger arrays. if (bucketSize > StackLargeBucket) { trimCount++; } if (Unsafe.SizeOf <T>() > StackModerateTypeSize) { trimCount++; } if (Unsafe.SizeOf <T>() > StackLargeTypeSize) { trimCount++; } break; case Utilities.MemoryPressure.Medium: trimCount = StackMediumTrimCount; break; } while (_count > 0 && trimCount-- > 0) { T[]? array = _arrays[--_count]; Debug.Assert(array is not null, "No nulls should have been present in slots < _count."); _arrays[_count] = null; if (log.IsEnabled()) { log.BufferTrimmed(array.GetHashCode(), array.Length, id); } } if (_count > 0 && _firstStackItemMS < uint.MaxValue - StackRefreshMS) { // Give the remaining items a bit more time _firstStackItemMS += StackRefreshMS; } } } }