From 23c844b2aa84a65e573dcc023d19b8f5294a8baf Mon Sep 17 00:00:00 2001 From: jhorv <38920027+jhorv@users.noreply.github.com> Date: Sat, 11 Mar 2023 15:05:48 -0500 Subject: [PATCH] Misc performance tweaks (#4509) * use Array.Empty() where instead of allocating new zero-length arrays * structure for loops in a way that the JIT will elide array/Span bounds checking * avoiding function calls in for loop condition tests * avoid LINQ in a hot path * conform with code style * fix mistake in GetNextWaitingObject() * fix GetNextWaitingObject() possibility of returning null if all list items have TimePoint == long.MaxValue * make GetNextWaitingObject() behave FIFO behavior for multiple items with the same TimePoint --- ARMeilleure/CodeGen/Arm64/CodeGenContext.cs | 2 +- .../RegisterAllocators/LinearScanAllocator.cs | 9 ++------ ARMeilleure/Decoders/DecoderHelper.cs | 4 ++-- ARMeilleure/Decoders/OpCodeTable.cs | 6 ++--- Ryujinx.Audio/Common/AudioDeviceSession.cs | 4 +++- Ryujinx.Ava/UI/Windows/IconColorPicker.cs | 2 +- Ryujinx.Graphics.Vulkan/Queries/Counters.cs | 2 +- Ryujinx.Graphics.Vulkan/SpecInfo.cs | 5 ++-- Ryujinx.Graphics.Vulkan/Window.cs | 5 ++-- Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs | 4 ++-- Ryujinx.HLE/HOS/Ipc/IpcMessage.cs | 5 ++-- Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs | 23 +++++++++++++++++-- .../Time/TimeZone/TimeZoneContentManager.cs | 2 +- Ryujinx.Input.SDL2/SDL2GamepadDriver.cs | 4 +++- .../MockVirtualMemoryManager.cs | 2 +- 15 files changed, 48 insertions(+), 31 deletions(-) diff --git a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs index 1ddde0c19..cebfbde12 100644 --- a/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs +++ b/ARMeilleure/CodeGen/Arm64/CodeGenContext.cs @@ -265,7 +265,7 @@ namespace ARMeilleure.CodeGen.Arm64 } else { - relocInfo = new RelocInfo(new RelocEntry[0]); + relocInfo = new RelocInfo(Array.Empty()); } return (code, relocInfo); diff --git a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs index 6ea62c28b..d80157afb 100644 --- a/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs +++ b/ARMeilleure/CodeGen/RegisterAllocators/LinearScanAllocator.cs @@ -433,16 +433,11 @@ namespace ARMeilleure.CodeGen.RegisterAllocators private static int GetHighestValueIndex(Span span) { - int highest = span[0]; - - if (highest == int.MaxValue) - { - return 0; - } + int highest = int.MinValue; int selected = 0; - for (int index = 1; index < span.Length; index++) + for (int index = 0; index < span.Length; index++) { int current = span[index]; diff --git a/ARMeilleure/Decoders/DecoderHelper.cs b/ARMeilleure/Decoders/DecoderHelper.cs index 38f98c39c..5227e6a19 100644 --- a/ARMeilleure/Decoders/DecoderHelper.cs +++ b/ARMeilleure/Decoders/DecoderHelper.cs @@ -17,7 +17,7 @@ namespace ARMeilleure.Decoders { uint[] tbl = new uint[256]; - for (int idx = 0; idx < 256; idx++) + for (int idx = 0; idx < tbl.Length; idx++) { tbl[idx] = ExpandImm8ToFP32((uint)idx); } @@ -29,7 +29,7 @@ namespace ARMeilleure.Decoders { ulong[] tbl = new ulong[256]; - for (int idx = 0; idx < 256; idx++) + for (int idx = 0; idx < tbl.Length; idx++) { tbl[idx] = ExpandImm8ToFP64((ulong)idx); } diff --git a/ARMeilleure/Decoders/OpCodeTable.cs b/ARMeilleure/Decoders/OpCodeTable.cs index 54abb1418..8464ce556 100644 --- a/ARMeilleure/Decoders/OpCodeTable.cs +++ b/ARMeilleure/Decoders/OpCodeTable.cs @@ -1301,7 +1301,7 @@ namespace ARMeilleure.Decoders { List[] temp = new List[FastLookupSize]; - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { temp[index] = new List(); } @@ -1311,7 +1311,7 @@ namespace ARMeilleure.Decoders int mask = ToFastLookupIndex(inst.Mask); int value = ToFastLookupIndex(inst.Value); - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { if ((index & mask) == value) { @@ -1320,7 +1320,7 @@ namespace ARMeilleure.Decoders } } - for (int index = 0; index < FastLookupSize; index++) + for (int index = 0; index < temp.Length; index++) { table[index] = temp[index].ToArray(); } diff --git a/Ryujinx.Audio/Common/AudioDeviceSession.cs b/Ryujinx.Audio/Common/AudioDeviceSession.cs index 07b0a8988..0191f7ccd 100644 --- a/Ryujinx.Audio/Common/AudioDeviceSession.cs +++ b/Ryujinx.Audio/Common/AudioDeviceSession.cs @@ -400,7 +400,9 @@ namespace Ryujinx.Audio.Common { uint bufferIndex = (_releasedBufferIndex - _bufferReleasedCount) % Constants.AudioDeviceBufferCountMax; - for (int i = 0; i < GetTotalBufferCount(); i++) + uint totalBufferCount = GetTotalBufferCount(); + + for (int i = 0; i < totalBufferCount; i++) { if (_buffers[bufferIndex].BufferTag == bufferTag) { diff --git a/Ryujinx.Ava/UI/Windows/IconColorPicker.cs b/Ryujinx.Ava/UI/Windows/IconColorPicker.cs index 9dca83eb6..a4c6287f3 100644 --- a/Ryujinx.Ava/UI/Windows/IconColorPicker.cs +++ b/Ryujinx.Ava/UI/Windows/IconColorPicker.cs @@ -125,7 +125,7 @@ namespace Ryujinx.Ava.UI.Windows public static Bgra32[] GetBuffer(Image image) { - return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : new Bgra32[0]; + return image.TryGetSinglePixelSpan(out var data) ? data.ToArray() : Array.Empty(); } private static int GetColorScore(Dictionary dominantColorBin, int maxHitCount, PaletteColor color) diff --git a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs index 7113d0601..d9d65062f 100644 --- a/Ryujinx.Graphics.Vulkan/Queries/Counters.cs +++ b/Ryujinx.Graphics.Vulkan/Queries/Counters.cs @@ -17,7 +17,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries _counterQueues = new CounterQueue[count]; - for (int index = 0; index < count; index++) + for (int index = 0; index < _counterQueues.Length; index++) { CounterType type = (CounterType)index; _counterQueues[index] = new CounterQueue(gd, device, pipeline, type); diff --git a/Ryujinx.Graphics.Vulkan/SpecInfo.cs b/Ryujinx.Graphics.Vulkan/SpecInfo.cs index 83a34cde0..4d226f615 100644 --- a/Ryujinx.Graphics.Vulkan/SpecInfo.cs +++ b/Ryujinx.Graphics.Vulkan/SpecInfo.cs @@ -29,7 +29,7 @@ namespace Ryujinx.Graphics.Vulkan uint structSize = 0; - for (int i = 0; i < count; ++i) + for (int i = 0; i < Map.Length; ++i) { var typeSize = SizeOf(description[i].Type); Map[i] = new SpecializationMapEntry(description[i].Id, structSize, typeSize); @@ -46,11 +46,10 @@ namespace Ryujinx.Graphics.Vulkan // For advanced mapping with overlapping or staggered fields public SpecDescription(SpecializationMapEntry[] map) { - int count = map.Length; Map = map; uint structSize = 0; - for (int i = 0; i < count; ++i) + for (int i = 0; i < map.Length; ++i) { structSize = Math.Max(structSize, map[i].Offset + (uint)map[i].Size); } diff --git a/Ryujinx.Graphics.Vulkan/Window.cs b/Ryujinx.Graphics.Vulkan/Window.cs index 5d6def3a9..075d1b303 100644 --- a/Ryujinx.Graphics.Vulkan/Window.cs +++ b/Ryujinx.Graphics.Vulkan/Window.cs @@ -60,10 +60,9 @@ namespace Ryujinx.Graphics.Vulkan private void RecreateSwapchain() { var oldSwapchain = _swapchain; - int imageCount = _swapchainImageViews.Length; _vsyncModeChanged = false; - for (int i = 0; i < imageCount; i++) + for (int i = 0; i < _swapchainImageViews.Length; i++) { _swapchainImageViews[i].Dispose(); } @@ -147,7 +146,7 @@ namespace Ryujinx.Graphics.Vulkan _swapchainImageViews = new Auto[imageCount]; - for (int i = 0; i < imageCount; i++) + for (int i = 0; i < _swapchainImageViews.Length; i++) { _swapchainImageViews[i] = CreateSwapchainImageView(_swapchainImages[i], surfaceFormat.Format); } diff --git a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs index 439590e0c..e6ed46138 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcHandleDesc.cs @@ -49,12 +49,12 @@ namespace Ryujinx.HLE.HOS.Ipc public static IpcHandleDesc MakeCopy(params int[] handles) { - return new IpcHandleDesc(handles, new int[0]); + return new IpcHandleDesc(handles, Array.Empty()); } public static IpcHandleDesc MakeMove(params int[] handles) { - return new IpcHandleDesc(new int[0], handles); + return new IpcHandleDesc(Array.Empty(), handles); } public byte[] GetBytes() diff --git a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs index 09e237fe5..55044da40 100644 --- a/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs +++ b/Ryujinx.HLE/HOS/Ipc/IpcMessage.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; @@ -132,7 +133,7 @@ namespace Ryujinx.HLE.HOS.Ipc word0 |= (ReceiveBuff.Count & 0xf) << 24; word0 |= (ExchangeBuff.Count & 0xf) << 28; - byte[] handleData = new byte[0]; + byte[] handleData = Array.Empty(); if (HandleDesc != null) { @@ -202,7 +203,7 @@ namespace Ryujinx.HLE.HOS.Ipc word0 |= (ReceiveBuff.Count & 0xf) << 24; word0 |= (ExchangeBuff.Count & 0xf) << 28; - byte[] handleData = new byte[0]; + byte[] handleData = Array.Empty(); if (HandleDesc != null) { diff --git a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs index 020048f4e..030a314f7 100644 --- a/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs +++ b/Ryujinx.HLE/HOS/Kernel/Common/KTimeManager.cs @@ -1,7 +1,6 @@ using Ryujinx.Common; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Common @@ -86,7 +85,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Common { Interlocked.Exchange(ref _enforceWakeupFromSpinWait, 0); - next = _waitingObjects.OrderBy(x => x.TimePoint).FirstOrDefault(); + next = GetNextWaitingObject(); } if (next != null) @@ -140,6 +139,26 @@ 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--) + { + WaitingObject current = _waitingObjects[index]; + + if (current.TimePoint <= lowestTimePoint) + { + selected = current; + lowestTimePoint = current.TimePoint; + } + } + + return selected; + } + public static long ConvertNanosecondsToMilliseconds(long time) { time /= 1000000; diff --git a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs index 69ed56d45..9367024e4 100644 --- a/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs +++ b/Ryujinx.HLE/HOS/Services/Time/TimeZone/TimeZoneContentManager.cs @@ -233,7 +233,7 @@ namespace Ryujinx.HLE.HOS.Services.Time.TimeZone // If the location name is too long, error out. if (locationName.Length > 0x24) { - outLocationNameArray = new string[0]; + outLocationNameArray = Array.Empty(); return ResultCode.LocationNameTooLong; } diff --git a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs index b20a76b84..d4086a105 100644 --- a/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs +++ b/Ryujinx.Input.SDL2/SDL2GamepadDriver.cs @@ -27,7 +27,9 @@ namespace Ryujinx.Input.SDL2 SDL2Driver.Instance.OnJoystickDisconnected += HandleJoyStickDisconnected; // Add already connected gamepads - for (int joystickIndex = 0; joystickIndex < SDL_NumJoysticks(); joystickIndex++) + int numJoysticks = SDL_NumJoysticks(); + + for (int joystickIndex = 0; joystickIndex < numJoysticks; joystickIndex++) { HandleJoyStickConnected(joystickIndex, SDL_JoystickGetDeviceInstanceID(joystickIndex)); } diff --git a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs index 6729f4a36..ef81a4615 100644 --- a/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs +++ b/Ryujinx.Memory.Tests/MockVirtualMemoryManager.cs @@ -78,7 +78,7 @@ namespace Ryujinx.Memory.Tests IEnumerable IVirtualMemoryManager.GetPhysicalRegions(ulong va, ulong size) { - return NoMappings ? new MemoryRange[0] : new MemoryRange[] { new MemoryRange(va, size) }; + return NoMappings ? Array.Empty() : new MemoryRange[] { new MemoryRange(va, size) }; } public bool IsMapped(ulong va)