aboutsummaryrefslogtreecommitdiff
path: root/Ryujinx.HLE/HOS
diff options
context:
space:
mode:
authorjhorv <38920027+jhorv@users.noreply.github.com>2023-04-23 22:06:23 -0400
committerGitHub <noreply@github.com>2023-04-24 02:06:23 +0000
commit666e05f5cb9aa9dc86874823c108586e7a5b38bd (patch)
tree0f4c2cf207e0c73c98c29148edc4cb9393a9e357 /Ryujinx.HLE/HOS
parent8d9d508dc78eb5225c99cb425fa484999f3c4305 (diff)
Reducing Memory Allocations 202303 (#4624)
* use ArrayPool, avoid 6000-7000 allocs/sec of runtime * use ArrayPool, avoid ~7k allocs/second during game execution * use ArrayPool, avoid ~3000 allocs/sec during game execution * use MemoryPool, reduce 0.5 MB/sec of new allocations during game execution * avoid over-allocation by setting List<> Capacity when known * remove LINQ in KTimeManager.UnscheduleFutureInvocation * KTimeManager - avoid spinning one more time when the time has arrived * KTimeManager - let SpinWait decide when to Thread.Yield(), and don't SpinOnce() immediately after Thread.Yield() * use MemoryPool, reduce ~175k bytes/sec allocation during game execution * IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array * Make ButtonMappingEntry a record struct to avoid allocations. Set the List<ButtonMappingEntry> capacity according to use. * add MemoryBuffer type for working with MemoryPool<byte> * update changes to use MemoryBuffer * make parameter ReadOnlySpan instead of Span * whitespace fix * Revert "IpcService - call commands via dynamic methods instead of reflection .Invoke(). Faster to call and with fewer allocations because parameters can be passed directly instead of as an array" This reverts commit f2c698bdf65f049e8481c9f2ec7138d9b9a8261d. * tweak KTimeManager spin behavior * replace MemoryBuffer with ByteMemoryPool modeled after System.Buffers.ArrayMemoryPool<T> * make ByteMemoryPoolBuffer responsible for renting memory
Diffstat (limited to 'Ryujinx.HLE/HOS')
-rw-r--r--Ryujinx.HLE/HOS/Ipc/IpcMessage.cs123
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs17
-rw-r--r--Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs7
-rw-r--r--Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs7
-rw-r--r--Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs35
-rw-r--r--Ryujinx.HLE/HOS/Services/IpcService.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/ServerBase.cs2
-rw-r--r--Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs21
8 files changed, 126 insertions, 88 deletions
diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
index 394bf888..21630c42 100644
--- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
+++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs
@@ -27,98 +27,103 @@ namespace Ryujinx.HLE.HOS.Ipc
public IpcMessage()
{
- PtrBuff = new List<IpcPtrBuffDesc>();
- SendBuff = new List<IpcBuffDesc>();
- ReceiveBuff = new List<IpcBuffDesc>();
- ExchangeBuff = new List<IpcBuffDesc>();
- RecvListBuff = new List<IpcRecvListBuffDesc>();
+ PtrBuff = new List<IpcPtrBuffDesc>(0);
+ SendBuff = new List<IpcBuffDesc>(0);
+ ReceiveBuff = new List<IpcBuffDesc>(0);
+ ExchangeBuff = new List<IpcBuffDesc>(0);
+ RecvListBuff = new List<IpcRecvListBuffDesc>(0);
- ObjectIds = new List<int>();
+ ObjectIds = new List<int>(0);
}
- public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
+ public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr)
{
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
{
BinaryReader reader = new BinaryReader(ms);
- Initialize(reader, cmdPtr);
- }
- }
+ int word0 = reader.ReadInt32();
+ int word1 = reader.ReadInt32();
- private void Initialize(BinaryReader reader, long cmdPtr)
- {
- int word0 = reader.ReadInt32();
- int word1 = reader.ReadInt32();
+ Type = (IpcMessageType)(word0 & 0xffff);
- Type = (IpcMessageType)(word0 & 0xffff);
+ int ptrBuffCount = (word0 >> 16) & 0xf;
+ int sendBuffCount = (word0 >> 20) & 0xf;
+ int recvBuffCount = (word0 >> 24) & 0xf;
+ int xchgBuffCount = (word0 >> 28) & 0xf;
- int ptrBuffCount = (word0 >> 16) & 0xf;
- int sendBuffCount = (word0 >> 20) & 0xf;
- int recvBuffCount = (word0 >> 24) & 0xf;
- int xchgBuffCount = (word0 >> 28) & 0xf;
+ int rawDataSize = (word1 >> 0) & 0x3ff;
+ int recvListFlags = (word1 >> 10) & 0xf;
+ bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
- int rawDataSize = (word1 >> 0) & 0x3ff;
- int recvListFlags = (word1 >> 10) & 0xf;
- bool hndDescEnable = ((word1 >> 31) & 0x1) != 0;
+ if (hndDescEnable)
+ {
+ HandleDesc = new IpcHandleDesc(reader);
+ }
- if (hndDescEnable)
- {
- HandleDesc = new IpcHandleDesc(reader);
- }
+ PtrBuff = new List<IpcPtrBuffDesc>(ptrBuffCount);
- for (int index = 0; index < ptrBuffCount; index++)
- {
- PtrBuff.Add(new IpcPtrBuffDesc(reader));
- }
+ for (int index = 0; index < ptrBuffCount; index++)
+ {
+ PtrBuff.Add(new IpcPtrBuffDesc(reader));
+ }
- void ReadBuff(List<IpcBuffDesc> buff, int count)
- {
- for (int index = 0; index < count; index++)
+ static List<IpcBuffDesc> ReadBuff(BinaryReader reader, int count)
{
- buff.Add(new IpcBuffDesc(reader));
+ List<IpcBuffDesc> buff = new List<IpcBuffDesc>(count);
+
+ for (int index = 0; index < count; index++)
+ {
+ buff.Add(new IpcBuffDesc(reader));
+ }
+
+ return buff;
}
- }
- ReadBuff(SendBuff, sendBuffCount);
- ReadBuff(ReceiveBuff, recvBuffCount);
- ReadBuff(ExchangeBuff, xchgBuffCount);
+ SendBuff = ReadBuff(reader, sendBuffCount);
+ ReceiveBuff = ReadBuff(reader, recvBuffCount);
+ ExchangeBuff = ReadBuff(reader, xchgBuffCount);
- rawDataSize *= 4;
+ rawDataSize *= 4;
- long recvListPos = reader.BaseStream.Position + rawDataSize;
+ long recvListPos = reader.BaseStream.Position + rawDataSize;
// Only CMIF has the padding requirements.
if (Type < IpcMessageType.TipcCloseSession)
{
long pad0 = GetPadSize16(reader.BaseStream.Position + cmdPtr);
- if (rawDataSize != 0)
- {
- rawDataSize -= (int)pad0;
+ if (rawDataSize != 0)
+ {
+ rawDataSize -= (int)pad0;
+ }
+
+ reader.BaseStream.Seek(pad0, SeekOrigin.Current);
}
- reader.BaseStream.Seek(pad0, SeekOrigin.Current);
- }
+ int recvListCount = recvListFlags - 2;
- int recvListCount = recvListFlags - 2;
+ if (recvListCount == 0)
+ {
+ recvListCount = 1;
+ }
+ else if (recvListCount < 0)
+ {
+ recvListCount = 0;
+ }
- if (recvListCount == 0)
- {
- recvListCount = 1;
- }
- else if (recvListCount < 0)
- {
- recvListCount = 0;
- }
+ RawData = reader.ReadBytes(rawDataSize);
- RawData = reader.ReadBytes(rawDataSize);
+ reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
- reader.BaseStream.Seek(recvListPos, SeekOrigin.Begin);
+ RecvListBuff = new List<IpcRecvListBuffDesc>(recvListCount);
- for (int index = 0; index < recvListCount; index++)
- {
- RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
+ for (int index = 0; index < recvListCount; index++)
+ {
+ RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
+ }
+
+ ObjectIds = new List<int>(0);
}
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
index 1af171b9..c0cd9ce9 100644
--- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs
@@ -71,7 +71,13 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
{
lock (_context.CriticalSection.Lock)
{
- _waitingObjects.RemoveAll(x => x.Object == schedulerObj);
+ for (int index = _waitingObjects.Count - 1; index >= 0; index--)
+ {
+ if (_waitingObjects[index].Object == schedulerObj)
+ {
+ _waitingObjects.RemoveAt(index);
+ }
+ }
}
}
@@ -105,16 +111,19 @@ namespace Ryujinx.HLE.HOS.Kernel.Common
}
else
{
- while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks <= next.TimePoint)
+ while (Interlocked.Read(ref _enforceWakeupFromSpinWait) != 1 && PerformanceCounter.ElapsedTicks < next.TimePoint)
{
+ // Our time is close - don't let SpinWait go off and potentially Thread.Sleep().
if (spinWait.NextSpinWillYield)
{
Thread.Yield();
spinWait.Reset();
}
-
- spinWait.SpinOnce();
+ else
+ {
+ spinWait.SpinOnce();
+ }
}
spinWait.Reset();
diff --git a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
index c6467208..3163c348 100644
--- a/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
+++ b/Ryujinx.HLE/HOS/Kernel/SupervisorCall/Syscall.cs
@@ -9,6 +9,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
+using System.Buffers;
using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
@@ -553,7 +554,9 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
KProcess currentProcess = KernelStatic.GetCurrentProcess();
- KSynchronizationObject[] syncObjs = handles.Length == 0 ? Array.Empty<KSynchronizationObject>() : new KSynchronizationObject[handles.Length];
+ KSynchronizationObject[] syncObjsArray = ArrayPool<KSynchronizationObject>.Shared.Rent(handles.Length);
+
+ Span<KSynchronizationObject> syncObjs = syncObjsArray.AsSpan(0, handles.Length);
for (int index = 0; index < handles.Length; index++)
{
@@ -606,6 +609,8 @@ namespace Ryujinx.HLE.HOS.Kernel.SupervisorCall
}
}
+ ArrayPool<KSynchronizationObject>.Shared.Return(syncObjsArray);
+
return result;
}
diff --git a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
index 973d5f6a..d42f9003 100644
--- a/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
+++ b/Ryujinx.HLE/HOS/Kernel/Threading/KSynchronization.cs
@@ -1,6 +1,7 @@
using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.Horizon.Common;
using System;
+using System.Buffers;
using System.Collections.Generic;
namespace Ryujinx.HLE.HOS.Kernel.Threading
@@ -59,7 +60,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
}
else
{
- LinkedListNode<KThread>[] syncNodes = syncObjs.Length == 0 ? Array.Empty<LinkedListNode<KThread>>() : new LinkedListNode<KThread>[syncObjs.Length];
+ LinkedListNode<KThread>[] syncNodesArray = ArrayPool<LinkedListNode<KThread>>.Shared.Rent(syncObjs.Length);
+
+ Span<LinkedListNode<KThread>> syncNodes = syncNodesArray.AsSpan(0, syncObjs.Length);
for (int index = 0; index < syncObjs.Length; index++)
{
@@ -101,6 +104,8 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
handleIndex = index;
}
}
+
+ ArrayPool<LinkedListNode<KThread>>.Shared.Return(syncNodesArray);
}
_context.CriticalSection.Leave();
diff --git a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
index 81321046..a137c413 100644
--- a/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
+++ b/Ryujinx.HLE/HOS/Services/Audio/AudioRenderer/AudioRendererServer.cs
@@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
+using Ryujinx.Common.Memory;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
@@ -68,25 +69,29 @@ namespace Ryujinx.HLE.HOS.Services.Audio.AudioRenderer
ReadOnlyMemory<byte> input = context.Memory.GetSpan(inputPosition, (int)inputSize).ToArray();
- Memory<byte> output = new byte[outputSize];
- Memory<byte> performanceOutput = new byte[performanceOutputSize];
+ using (IMemoryOwner<byte> outputOwner = ByteMemoryPool.Shared.RentCleared(outputSize))
+ using (IMemoryOwner<byte> performanceOutputOwner = ByteMemoryPool.Shared.RentCleared(performanceOutputSize))
+ {
+ Memory<byte> output = outputOwner.Memory;
+ Memory<byte> performanceOutput = performanceOutputOwner.Memory;
- using MemoryHandle outputHandle = output.Pin();
- using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
+ using MemoryHandle outputHandle = output.Pin();
+ using MemoryHandle performanceOutputHandle = performanceOutput.Pin();
- ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
+ ResultCode result = _impl.RequestUpdate(output, performanceOutput, input);
- if (result == ResultCode.Success)
- {
- context.Memory.Write(outputPosition, output.Span);
- context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
- }
- else
- {
- Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
- }
+ if (result == ResultCode.Success)
+ {
+ context.Memory.Write(outputPosition, output.Span);
+ context.Memory.Write(performanceOutputPosition, performanceOutput.Span);
+ }
+ else
+ {
+ Logger.Error?.Print(LogClass.ServiceAudio, $"Error while processing renderer update: 0x{(int)result:X}");
+ }
- return result;
+ return result;
+ }
}
[CommandCmif(5)]
diff --git a/Ryujinx.HLE/HOS/Services/IpcService.cs b/Ryujinx.HLE/HOS/Services/IpcService.cs
index f42fd0e2..048a68a9 100644
--- a/Ryujinx.HLE/HOS/Services/IpcService.cs
+++ b/Ryujinx.HLE/HOS/Services/IpcService.cs
@@ -76,6 +76,8 @@ namespace Ryujinx.HLE.HOS.Services
context.RequestData.BaseStream.Seek(0x10 + dataPayloadSize, SeekOrigin.Begin);
+ context.Request.ObjectIds.EnsureCapacity(inputObjCount);
+
for (int index = 0; index < inputObjCount; index++)
{
context.Request.ObjectIds.Add(context.RequestData.ReadInt32());
diff --git a/Ryujinx.HLE/HOS/Services/ServerBase.cs b/Ryujinx.HLE/HOS/Services/ServerBase.cs
index 762b4fa4..b994679a 100644
--- a/Ryujinx.HLE/HOS/Services/ServerBase.cs
+++ b/Ryujinx.HLE/HOS/Services/ServerBase.cs
@@ -217,6 +217,8 @@ namespace Ryujinx.HLE.HOS.Services
if (noReceive)
{
+ response.PtrBuff.EnsureCapacity(request.RecvListBuff.Count);
+
for (int i = 0; i < request.RecvListBuff.Count; i++)
{
ulong size = (ulong)BinaryPrimitives.ReadInt16LittleEndian(request.RawData.AsSpan(sizesOffset + i * 2, 2));
diff --git a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
index 0724c83e..42fc2761 100644
--- a/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
+++ b/Ryujinx.HLE/HOS/Services/SurfaceFlinger/IHOSBinderDriver.cs
@@ -1,7 +1,9 @@
-using Ryujinx.HLE.HOS.Ipc;
+using Ryujinx.Common.Memory;
+using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon.Common;
using System;
+using System.Buffers;
namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
{
@@ -83,16 +85,19 @@ namespace Ryujinx.HLE.HOS.Services.SurfaceFlinger
ReadOnlySpan<byte> inputParcel = context.Memory.GetSpan(dataPos, (int)dataSize);
- Span<byte> outputParcel = new Span<byte>(new byte[replySize]);
+ using (IMemoryOwner<byte> outputParcelOwner = ByteMemoryPool.Shared.RentCleared(replySize))
+ {
+ Span<byte> outputParcel = outputParcelOwner.Memory.Span;
+
+ ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
- ResultCode result = OnTransact(binderId, code, flags, inputParcel, outputParcel);
+ if (result == ResultCode.Success)
+ {
+ context.Memory.Write(replyPos, outputParcel);
+ }
- if (result == ResultCode.Success)
- {
- context.Memory.Write(replyPos, outputParcel);
+ return result;
}
-
- return result;
}
protected abstract ResultCode AdjustRefcount(int binderId, int addVal, int type);