[scudo][standalone] Skip irrelevant regions during release

With the 'new' way of releasing on 32-bit, we iterate through all the
regions in between `First` and `Last`, which covers regions that do not
belong to the class size we are working with. This is effectively wasted
cycles.

With this change, we add a `SkipRegion` lambda to `releaseFreeMemoryToOS`
that will allow the release function to know when to skip a region.
For the 64-bit primary, since we are only working with 1 region, we never
skip.

Reviewed By: hctim

Differential Revision: https://reviews.llvm.org/D86399
This commit is contained in:
Kostya Kortchinsky 2020-08-24 14:13:12 -07:00
parent 1dc57ada0c
commit bd5ca4f0ed
4 changed files with 26 additions and 6 deletions

View File

@ -483,12 +483,15 @@ private:
} }
} }
uptr TotalReleasedBytes = 0; uptr TotalReleasedBytes = 0;
auto SkipRegion = [this, First, ClassId](uptr RegionIndex) {
return (PossibleRegions[First + RegionIndex] - 1U) != ClassId;
};
if (First && Last) { if (First && Last) {
const uptr Base = First * RegionSize; const uptr Base = First * RegionSize;
const uptr NumberOfRegions = Last - First + 1U; const uptr NumberOfRegions = Last - First + 1U;
ReleaseRecorder Recorder(Base); ReleaseRecorder Recorder(Base);
releaseFreeMemoryToOS(Sci->FreeList, Base, RegionSize, NumberOfRegions, releaseFreeMemoryToOS(Sci->FreeList, Base, RegionSize, NumberOfRegions,
BlockSize, &Recorder); BlockSize, &Recorder, SkipRegion);
if (Recorder.getReleasedRangesCount() > 0) { if (Recorder.getReleasedRangesCount() > 0) {
Sci->ReleaseInfo.PushedBlocksAtLastRelease = Sci->Stats.PushedBlocks; Sci->ReleaseInfo.PushedBlocksAtLastRelease = Sci->Stats.PushedBlocks;
Sci->ReleaseInfo.RangesReleased += Recorder.getReleasedRangesCount(); Sci->ReleaseInfo.RangesReleased += Recorder.getReleasedRangesCount();

View File

@ -479,9 +479,11 @@ private:
} }
} }
auto SkipRegion = [](UNUSED uptr RegionIndex) { return false; };
ReleaseRecorder Recorder(Region->RegionBeg, &Region->Data); ReleaseRecorder Recorder(Region->RegionBeg, &Region->Data);
releaseFreeMemoryToOS(Region->FreeList, Region->RegionBeg, releaseFreeMemoryToOS(Region->FreeList, Region->RegionBeg,
Region->AllocatedUser, 1U, BlockSize, &Recorder); Region->AllocatedUser, 1U, BlockSize, &Recorder,
SkipRegion);
if (Recorder.getReleasedRangesCount() > 0) { if (Recorder.getReleasedRangesCount() > 0) {
Region->ReleaseInfo.PushedBlocksAtLastRelease = Region->ReleaseInfo.PushedBlocksAtLastRelease =

View File

@ -156,6 +156,11 @@ public:
CurrentPage++; CurrentPage++;
} }
void skipPages(uptr N) {
closeOpenedRange();
CurrentPage += N;
}
void finish() { closeOpenedRange(); } void finish() { closeOpenedRange(); }
private: private:
@ -174,11 +179,11 @@ private:
uptr CurrentRangeStatePage = 0; uptr CurrentRangeStatePage = 0;
}; };
template <class TransferBatchT, class ReleaseRecorderT> template <class TransferBatchT, class ReleaseRecorderT, typename SkipRegionT>
NOINLINE void NOINLINE void
releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> &FreeList, uptr Base, releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> &FreeList, uptr Base,
uptr RegionSize, uptr NumberOfRegions, uptr BlockSize, uptr RegionSize, uptr NumberOfRegions, uptr BlockSize,
ReleaseRecorderT *Recorder) { ReleaseRecorderT *Recorder, SkipRegionT SkipRegion) {
const uptr PageSize = getPageSizeCached(); const uptr PageSize = getPageSizeCached();
// Figure out the number of chunks per page and whether we can take a fast // Figure out the number of chunks per page and whether we can take a fast
@ -283,10 +288,15 @@ releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> &FreeList, uptr Base,
FreePagesRangeTracker<ReleaseRecorderT> RangeTracker(Recorder); FreePagesRangeTracker<ReleaseRecorderT> RangeTracker(Recorder);
if (SameBlockCountPerPage) { if (SameBlockCountPerPage) {
// Fast path, every page has the same number of chunks affecting it. // Fast path, every page has the same number of chunks affecting it.
for (uptr I = 0; I < NumberOfRegions; I++) for (uptr I = 0; I < NumberOfRegions; I++) {
if (SkipRegion(I)) {
RangeTracker.skipPages(PagesCount);
continue;
}
for (uptr J = 0; J < PagesCount; J++) for (uptr J = 0; J < PagesCount; J++)
RangeTracker.processNextPage(Counters.get(I, J) == RangeTracker.processNextPage(Counters.get(I, J) ==
FullPagesBlockCountMax); FullPagesBlockCountMax);
}
} else { } else {
// Slow path, go through the pages keeping count how many chunks affect // Slow path, go through the pages keeping count how many chunks affect
// each page. // each page.
@ -298,6 +308,10 @@ releaseFreeMemoryToOS(const IntrusiveList<TransferBatchT> &FreeList, uptr Base,
// up the number of chunks on the current page and checking on every step // up the number of chunks on the current page and checking on every step
// whether the page boundary was crossed. // whether the page boundary was crossed.
for (uptr I = 0; I < NumberOfRegions; I++) { for (uptr I = 0; I < NumberOfRegions; I++) {
if (SkipRegion(I)) {
RangeTracker.skipPages(PagesCount);
continue;
}
uptr PrevPageBoundary = 0; uptr PrevPageBoundary = 0;
uptr CurrentBoundary = 0; uptr CurrentBoundary = 0;
for (uptr J = 0; J < PagesCount; J++) { for (uptr J = 0; J < PagesCount; J++) {

View File

@ -190,9 +190,10 @@ template <class SizeClassMap> void testReleaseFreeMemoryToOS() {
} }
// Release the memory. // Release the memory.
auto SkipRegion = [](UNUSED scudo::uptr RegionIndex) { return false; };
ReleasedPagesRecorder Recorder; ReleasedPagesRecorder Recorder;
releaseFreeMemoryToOS(FreeList, 0, MaxBlocks * BlockSize, 1U, BlockSize, releaseFreeMemoryToOS(FreeList, 0, MaxBlocks * BlockSize, 1U, BlockSize,
&Recorder); &Recorder, SkipRegion);
// Verify that there are no released pages touched by used chunks and all // Verify that there are no released pages touched by used chunks and all
// ranges of free chunks big enough to contain the entire memory pages had // ranges of free chunks big enough to contain the entire memory pages had