Reducing memory allocations (#4537)

* add RecyclableMemoryStream dependency and MemoryStreamManager

* organize BinaryReader/BinaryWriter extensions

* add StreamExtensions to reduce need for BinaryWriter

* simple replacments of MemoryStream with RecyclableMemoryStream

* add write ReadOnlySequence<byte> support to IVirtualMemoryManager

* avoid 0-length array creation

* rework IpcMessage and related types to greatly reduce memory allocation by using RecylableMemoryStream, keeping streams around longer, avoiding their creation when possible, and avoiding creation of BinaryReader and BinaryWriter when possible

* reduce LINQ-induced memory allocations with custom methods to query KPriorityQueue

* use RecyclableMemoryStream in StreamUtils, and use StreamUtils in EmbeddedResources

* add constants for nanosecond/millisecond conversions

* code formatting

* XML doc adjustments

* fix: StreamExtension.WriteByte not writing non-zero values for lengths <= 16

* XML Doc improvements. Implement StreamExtensions.WriteByte() block writes for large-enough count values.

* add copyless path for StreamExtension.Write(ReadOnlySpan<int>)

* add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence<byte>); remove previous explicit implementations

* code style fixes

* remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator
This commit is contained in:
jhorv 2023-03-17 08:14:50 -04:00 committed by GitHub
parent 7870423671
commit 5131b71437
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 838 additions and 426 deletions

View file

@ -16,7 +16,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public WaitingObject(IKFutureSchedulerObject schedulerObj, long timePoint)
{
Object = schedulerObj;
Object = schedulerObj;
TimePoint = timePoint;
}
}
@ -27,6 +27,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private bool _keepRunning;
private long _enforceWakeupFromSpinWait;
private const long NanosecondsPerSecond = 1000000000L;
private const long NanosecondsPerMillisecond = 1000000L;
public KTimeManager(KernelContext context)
{
_context = context;
@ -55,7 +58,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
_waitingObjects.Add(new WaitingObject(schedulerObj, timePoint));
if (timeout < 1000000)
if (timeout < NanosecondsPerMillisecond)
{
Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 1);
}
@ -142,7 +145,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
private WaitingObject GetNextWaitingObject()
{
WaitingObject selected = null;
long lowestTimePoint = long.MaxValue;
for (int index = _waitingObjects.Count - 1; index >= 0; index--)
@ -161,7 +164,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertNanosecondsToMilliseconds(long time)
{
time /= 1000000;
time /= NanosecondsPerMillisecond;
if ((ulong)time > int.MaxValue)
{
@ -173,18 +176,18 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
public static long ConvertMillisecondsToNanoseconds(long time)
{
return time * 1000000;
return time * NanosecondsPerMillisecond;
}
public static long ConvertNanosecondsToHostTicks(long ns)
{
long nsDiv = ns / 1000000000;
long nsMod = ns % 1000000000;
long tickDiv = PerformanceCounter.TicksPerSecond / 1000000000;
long tickMod = PerformanceCounter.TicksPerSecond % 1000000000;
long nsDiv = ns / NanosecondsPerSecond;
long nsMod = ns % NanosecondsPerSecond;
long tickDiv = PerformanceCounter.TicksPerSecond / NanosecondsPerSecond;
long tickMod = PerformanceCounter.TicksPerSecond % NanosecondsPerSecond;
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / 1000000000;
return (nsDiv * tickDiv) * 1000000000 + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
long baseTicks = (nsMod * tickMod + PerformanceCounter.TicksPerSecond - 1) / NanosecondsPerSecond;
return (nsDiv * tickDiv) * NanosecondsPerSecond + nsDiv * tickMod + nsMod * tickDiv + baseTicks;
}
public static long ConvertGuestTicksToNanoseconds(long ticks)