[GpuMemory] Scan possibly CPU-dirty blocks 64 at a time
ForEachPossiblyCpuDirtyRange combined a region lookup and a summary read for every 4 MiB block of mapped memory on each device-address sweep. A bitset of blocks that have a region and the dirty summary words are now scanned a word at a time, with the same runs as before.
This commit is contained in:
parent
87c5195ba3
commit
f2d55afe69
|
|
@ -33,4 +33,6 @@ public sealed class CpuDirtySummary
|
|||
}
|
||||
|
||||
public bool IsDirty(ulong block) => (Volatile.Read(ref _words[block / WordBits]) & (1L << (int)(block % WordBits))) != 0;
|
||||
|
||||
public ulong Word(int index) => (ulong)Volatile.Read(ref _words[index]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ public sealed class GuestPageTracker
|
|||
|
||||
private readonly PageGuard _pages;
|
||||
private readonly TrackedRegion?[] _regions = new TrackedRegion?[TrackerLayout.BlockCount];
|
||||
private readonly long[] _presentRegions = new long[(TrackerLayout.BlockCount + 63) / 64];
|
||||
private readonly object _regionGate = new();
|
||||
private readonly CpuDirtySummary _cpuDirtySummary = new();
|
||||
|
||||
|
|
@ -448,19 +449,42 @@ public sealed class GuestPageTracker
|
|||
var last = (end - 1) / BlockBytes;
|
||||
var runStart = 0UL;
|
||||
var inRun = false;
|
||||
for (var index = vaddr / BlockBytes; index <= last; index++)
|
||||
var index = vaddr / BlockBytes;
|
||||
var loadedWord = -1;
|
||||
var possiblyDirty = 0UL;
|
||||
while (index <= last)
|
||||
{
|
||||
var possiblyDirty = Volatile.Read(ref _regions[index]) == null || _cpuDirtySummary.IsDirty(index);
|
||||
if (possiblyDirty && !inRun)
|
||||
var word = (int)(index / 64);
|
||||
if (word != loadedWord)
|
||||
{
|
||||
runStart = Math.Max(vaddr, index * BlockBytes);
|
||||
inRun = true;
|
||||
loadedWord = word;
|
||||
possiblyDirty = ~(ulong)Volatile.Read(ref _presentRegions[word]) | _cpuDirtySummary.Word(word);
|
||||
}
|
||||
else if (!possiblyDirty && inRun)
|
||||
|
||||
var shift = (int)(index % 64);
|
||||
var pending = (inRun ? ~possiblyDirty : possiblyDirty) >> shift;
|
||||
if (pending == 0)
|
||||
{
|
||||
index = ((ulong)word + 1) * 64;
|
||||
continue;
|
||||
}
|
||||
|
||||
index += (ulong)System.Numerics.BitOperations.TrailingZeroCount(pending);
|
||||
if (index > last)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (inRun)
|
||||
{
|
||||
visit(runStart, index * BlockBytes - runStart);
|
||||
inRun = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
runStart = Math.Max(vaddr, index * BlockBytes);
|
||||
inRun = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inRun)
|
||||
|
|
@ -524,6 +548,7 @@ public sealed class GuestPageTracker
|
|||
|
||||
var created = new TrackedRegion(_pages, index * BlockBytes, _cpuDirtySummary);
|
||||
Volatile.Write(ref _regions[index], created);
|
||||
Interlocked.Or(ref _presentRegions[index / 64], 1L << (int)(index % 64));
|
||||
return created;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,32 @@ public sealed class GuestPageTrackerTests : IDisposable
|
|||
Release(address, Region * 3);
|
||||
}
|
||||
|
||||
[NativePageProtectionFact]
|
||||
public void PossiblyCpuDirtyRangesCrossSummaryWords()
|
||||
{
|
||||
var address = AllocateAligned(Region * 2, Region * 64);
|
||||
List<(ulong Address, ulong Size)> Runs(ulong start, ulong size)
|
||||
{
|
||||
var runs = new List<(ulong Address, ulong Size)>();
|
||||
_tracker.ForEachPossiblyCpuDirtyRange(start, size, (runAddress, runSize) => runs.Add((runAddress, runSize)));
|
||||
return runs;
|
||||
}
|
||||
|
||||
_tracker.ForEachUploadRange(address, Region * 2, false, NoRange, NoUpload, preserveCpuWriteHotPages: false);
|
||||
Assert.Equal([(address - Region * 2 + Page, Region * 2 - Page)], Runs(address - Region * 2 + Page, Region * 4 - Page * 2));
|
||||
|
||||
_tracker.MarkCpuDirtyPages(address + Region + Page, 8);
|
||||
Assert.Equal([(address - Region * 2, Region * 2), (address + Region, Region - Page)], Runs(address - Region * 2, Region * 4 - Page));
|
||||
Assert.Equal([(address + Region, Region)], Runs(address, Region * 2));
|
||||
Assert.Equal([(address + Region, Region * 64)], Runs(address, Region * 65));
|
||||
|
||||
_tracker.ForEachUploadRange(address, Region * 2, false, NoRange, NoUpload, preserveCpuWriteHotPages: false);
|
||||
Assert.Empty(Runs(address, Region * 2));
|
||||
Assert.Equal([(address + Region * 2, Region * 63)], Runs(address, Region * 65));
|
||||
_tracker.UntrackMemory(address, Region * 2);
|
||||
Release(address, Region * 2);
|
||||
}
|
||||
|
||||
[NativePageProtectionFact]
|
||||
public void RangeInvalidationBatchesOwnershipTransferAcrossRegions()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue