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

@ -1,3 +1,6 @@
using Microsoft.IO;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.IO;
@ -18,20 +21,27 @@ namespace Ryujinx.HLE.HOS.Ipc
HasPId = (word & 1) != 0;
ToCopy = new int[(word >> 1) & 0xf];
ToMove = new int[(word >> 5) & 0xf];
PId = HasPId ? reader.ReadUInt64() : 0;
for (int index = 0; index < ToCopy.Length; index++)
int toCopySize = (word >> 1) & 0xf;
int[] toCopy = toCopySize == 0 ? Array.Empty<int>() : new int[toCopySize];
for (int index = 0; index < toCopy.Length; index++)
{
ToCopy[index] = reader.ReadInt32();
toCopy[index] = reader.ReadInt32();
}
for (int index = 0; index < ToMove.Length; index++)
ToCopy = toCopy;
int toMoveSize = (word >> 5) & 0xf;
int[] toMove = toMoveSize == 0 ? Array.Empty<int>() : new int[toMoveSize];
for (int index = 0; index < toMove.Length; index++)
{
ToMove[index] = reader.ReadInt32();
toMove[index] = reader.ReadInt32();
}
ToMove = toMove;
}
public IpcHandleDesc(int[] copy, int[] move)
@ -57,36 +67,27 @@ namespace Ryujinx.HLE.HOS.Ipc
return new IpcHandleDesc(Array.Empty<int>(), handles);
}
public byte[] GetBytes()
public RecyclableMemoryStream GetStream()
{
using (MemoryStream ms = new MemoryStream())
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word = HasPId ? 1 : 0;
word |= (ToCopy.Length & 0xf) << 1;
word |= (ToMove.Length & 0xf) << 5;
ms.Write(word);
if (HasPId)
{
BinaryWriter writer = new BinaryWriter(ms);
int word = HasPId ? 1 : 0;
word |= (ToCopy.Length & 0xf) << 1;
word |= (ToMove.Length & 0xf) << 5;
writer.Write(word);
if (HasPId)
{
writer.Write(PId);
}
foreach (int handle in ToCopy)
{
writer.Write(handle);
}
foreach (int handle in ToMove)
{
writer.Write(handle);
}
return ms.ToArray();
ms.Write(PId);
}
ms.Write(ToCopy);
ms.Write(ToMove);
ms.Position = 0;
return ms;
}
}
}

View file

@ -1,4 +1,8 @@
using Microsoft.IO;
using Ryujinx.Common;
using Ryujinx.Common.Memory;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
@ -32,9 +36,9 @@ namespace Ryujinx.HLE.HOS.Ipc
ObjectIds = new List<int>();
}
public IpcMessage(byte[] data, long cmdPtr) : this()
public IpcMessage(ReadOnlySpan<byte> data, long cmdPtr) : this()
{
using (MemoryStream ms = new MemoryStream(data))
using (RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream(data))
{
BinaryReader reader = new BinaryReader(ms);
@ -114,124 +118,119 @@ namespace Ryujinx.HLE.HOS.Ipc
for (int index = 0; index < recvListCount; index++)
{
RecvListBuff.Add(new IpcRecvListBuffDesc(reader));
RecvListBuff.Add(new IpcRecvListBuffDesc(reader.ReadUInt64()));
}
}
public byte[] GetBytes(long cmdPtr, ulong recvListAddr)
public RecyclableMemoryStream GetStream(long cmdPtr, ulong recvListAddr)
{
using (MemoryStream ms = new MemoryStream())
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word0;
int word1;
word0 = (int)Type;
word0 |= (PtrBuff.Count & 0xf) << 16;
word0 |= (SendBuff.Count & 0xf) << 20;
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
int dataLength = RawData?.Length ?? 0;
dataLength = (dataLength + 3) & ~3;
int rawLength = dataLength;
int pad0 = (int)GetPadSize16(cmdPtr + 8 + (handleDataStream?.Length ?? 0) + PtrBuff.Count * 8);
// Apparently, padding after Raw Data is 16 bytes, however when there is
// padding before Raw Data too, we need to subtract the size of this padding.
// This is the weirdest padding I've seen so far...
int pad1 = 0x10 - pad0;
dataLength = (dataLength + pad0 + pad1) / 4;
word1 = (dataLength & 0x3ff) | (2 << 10);
if (HandleDesc != null)
{
BinaryWriter writer = new BinaryWriter(ms);
int word0;
int word1;
word0 = (int)Type;
word0 |= (PtrBuff.Count & 0xf) << 16;
word0 |= (SendBuff.Count & 0xf) << 20;
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
byte[] handleData = Array.Empty<byte>();
if (HandleDesc != null)
{
handleData = HandleDesc.GetBytes();
}
int dataLength = RawData?.Length ?? 0;
dataLength = (dataLength + 3) & ~3;
int rawLength = dataLength;
int pad0 = (int)GetPadSize16(cmdPtr + 8 + handleData.Length + PtrBuff.Count * 8);
// Apparently, padding after Raw Data is 16 bytes, however when there is
// padding before Raw Data too, we need to subtract the size of this padding.
// This is the weirdest padding I've seen so far...
int pad1 = 0x10 - pad0;
dataLength = (dataLength + pad0 + pad1) / 4;
word1 = (dataLength & 0x3ff) | (2 << 10);
if (HandleDesc != null)
{
word1 |= 1 << 31;
}
writer.Write(word0);
writer.Write(word1);
writer.Write(handleData);
for (int index = 0; index < PtrBuff.Count; index++)
{
writer.Write(PtrBuff[index].GetWord0());
writer.Write(PtrBuff[index].GetWord1());
}
ms.Seek(pad0, SeekOrigin.Current);
if (RawData != null)
{
writer.Write(RawData);
ms.Seek(rawLength - RawData.Length, SeekOrigin.Current);
}
writer.Write(new byte[pad1]);
writer.Write(recvListAddr);
return ms.ToArray();
word1 |= 1 << 31;
}
ms.Write(word0);
ms.Write(word1);
if (handleDataStream != null)
{
ms.Write(handleDataStream);
}
foreach (IpcPtrBuffDesc ptrBuffDesc in PtrBuff)
{
ms.Write(ptrBuffDesc.GetWord0());
ms.Write(ptrBuffDesc.GetWord1());
}
ms.WriteByte(0, pad0);
if (RawData != null)
{
ms.Write(RawData);
ms.WriteByte(0, rawLength - RawData.Length);
}
ms.WriteByte(0, pad1);
ms.Write(recvListAddr);
ms.Position = 0;
return ms;
}
public byte[] GetBytesTipc()
public RecyclableMemoryStream GetStreamTipc()
{
Debug.Assert(PtrBuff.Count == 0);
using (MemoryStream ms = new MemoryStream())
RecyclableMemoryStream ms = MemoryStreamManager.Shared.GetStream();
int word0;
int word1;
word0 = (int)Type;
word0 |= (SendBuff.Count & 0xf) << 20;
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
using RecyclableMemoryStream handleDataStream = HandleDesc?.GetStream();
int dataLength = RawData?.Length ?? 0;
dataLength = ((dataLength + 3) & ~3) / 4;
word1 = (dataLength & 0x3ff);
if (HandleDesc != null)
{
BinaryWriter writer = new BinaryWriter(ms);
int word0;
int word1;
word0 = (int)Type;
word0 |= (SendBuff.Count & 0xf) << 20;
word0 |= (ReceiveBuff.Count & 0xf) << 24;
word0 |= (ExchangeBuff.Count & 0xf) << 28;
byte[] handleData = Array.Empty<byte>();
if (HandleDesc != null)
{
handleData = HandleDesc.GetBytes();
}
int dataLength = RawData?.Length ?? 0;
dataLength = ((dataLength + 3) & ~3) / 4;
word1 = (dataLength & 0x3ff);
if (HandleDesc != null)
{
word1 |= 1 << 31;
}
writer.Write(word0);
writer.Write(word1);
writer.Write(handleData);
if (RawData != null)
{
writer.Write(RawData);
}
return ms.ToArray();
word1 |= 1 << 31;
}
ms.Write(word0);
ms.Write(word1);
if (handleDataStream != null)
{
ms.Write(handleDataStream);
}
if (RawData != null)
{
ms.Write(RawData);
}
return ms;
}
private long GetPadSize16(long position)

View file

@ -13,13 +13,11 @@ namespace Ryujinx.HLE.HOS.Ipc
Size = size;
}
public IpcRecvListBuffDesc(BinaryReader reader)
public IpcRecvListBuffDesc(ulong packedValue)
{
ulong value = reader.ReadUInt64();
Position = packedValue & 0xffffffffffff;
Position = value & 0xffffffffffff;
Size = (ushort)(value >> 48);
Size = (ushort)(packedValue >> 48);
}
}
}