From 5131b71437b36f9b876046a2fddd5465652e0f10 Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Fri, 17 Mar 2023 08:14:50 -0400 Subject: 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 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) * add default implementation of IVirtualMemoryManager.Write(ulong, ReadOnlySequence); remove previous explicit implementations * code style fixes * remove LINQ completely from KScheduler/KPriorityQueue by implementing a custom struct-based enumerator --- Ryujinx.Common/Extensions/StreamExtensions.cs | 138 ++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 Ryujinx.Common/Extensions/StreamExtensions.cs (limited to 'Ryujinx.Common/Extensions/StreamExtensions.cs') diff --git a/Ryujinx.Common/Extensions/StreamExtensions.cs b/Ryujinx.Common/Extensions/StreamExtensions.cs new file mode 100644 index 00000000..f6fc870a --- /dev/null +++ b/Ryujinx.Common/Extensions/StreamExtensions.cs @@ -0,0 +1,138 @@ +using System; +using System.Buffers.Binary; +using System.IO; +using System.Runtime.InteropServices; + +namespace Ryujinx.Common +{ + public static class StreamExtensions + { + /// + /// Writes a " /> to this stream. + /// + /// This default implementation converts each buffer value to a stack-allocated + /// byte array, then writes it to the Stream using . + /// + /// The stream to be written to + /// The buffer of values to be written + public static void Write(this Stream stream, ReadOnlySpan buffer) + { + if (buffer.Length == 0) + { + return; + } + + if (BitConverter.IsLittleEndian) + { + ReadOnlySpan byteBuffer = MemoryMarshal.Cast(buffer); + stream.Write(byteBuffer); + } + else + { + Span byteBuffer = stackalloc byte[sizeof(int)]; + + foreach (int value in buffer) + { + BinaryPrimitives.WriteInt32LittleEndian(byteBuffer, value); + stream.Write(byteBuffer); + } + } + } + + /// + /// Writes a four-byte signed integer to this stream. The current position + /// of the stream is advanced by four. + /// + /// The stream to be written to + /// The value to be written + public static void Write(this Stream stream, int value) + { + Span buffer = stackalloc byte[sizeof(int)]; + BinaryPrimitives.WriteInt32LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// + /// Writes an eight-byte signed integer to this stream. The current position + /// of the stream is advanced by eight. + /// + /// The stream to be written to + /// The value to be written + public static void Write(this Stream stream, long value) + { + Span buffer = stackalloc byte[sizeof(long)]; + BinaryPrimitives.WriteInt64LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// + // Writes a four-byte unsigned integer to this stream. The current position + // of the stream is advanced by four. + /// + /// The stream to be written to + /// The value to be written + public static void Write(this Stream stream, uint value) + { + Span buffer = stackalloc byte[sizeof(uint)]; + BinaryPrimitives.WriteUInt32LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// + /// Writes an eight-byte unsigned integer to this stream. The current + /// position of the stream is advanced by eight. + /// + /// The stream to be written to + /// The value to be written + public static void Write(this Stream stream, ulong value) + { + Span buffer = stackalloc byte[sizeof(ulong)]; + BinaryPrimitives.WriteUInt64LittleEndian(buffer, value); + stream.Write(buffer); + } + + /// + /// Writes the contents of source to stream by calling source.CopyTo(stream). + /// Provides consistency with other Stream.Write methods. + /// + /// The stream to be written to + /// The stream to be read from + public static void Write(this Stream stream, Stream source) + { + source.CopyTo(stream); + } + + /// + /// Writes a sequence of bytes to the Stream. + /// + /// The stream to be written to. + /// The byte to be written + /// The number of times the value should be written + public static void WriteByte(this Stream stream, byte value, int count) + { + if (count <= 0) + { + return; + } + + const int BlockSize = 16; + + int blockCount = count / BlockSize; + if (blockCount > 0) + { + Span span = stackalloc byte[BlockSize]; + span.Fill(value); + for (int x = 0; x < blockCount; x++) + { + stream.Write(span); + } + } + + int nonBlockBytes = count % BlockSize; + for (int x = 0; x < nonBlockBytes; x++) + { + stream.WriteByte(value); + } + } + } +} -- cgit v1.2.3