-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make ConstructRid 5x faster and AOT-compatible
- Loading branch information
Showing
9 changed files
with
263 additions
and
42 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,45 +1,14 @@ | ||
using Godot; | ||
using System; | ||
#if GODOT_PC | ||
using System.Reflection; | ||
using System.Reflection.Emit; | ||
#else | ||
using System.Diagnostics; | ||
using System.Runtime.InteropServices; | ||
#endif | ||
|
||
namespace ImGuiGodot.Internal; | ||
|
||
internal static class Util | ||
{ | ||
#if GODOT_PC | ||
// this is ~15x faster, so keep it for non-AOT platforms | ||
|
||
public static readonly Func<ulong, Rid> ConstructRid; | ||
|
||
static Util() | ||
{ | ||
ConstructorInfo cinfo = typeof(Rid).GetConstructor( | ||
BindingFlags.NonPublic | BindingFlags.Instance, | ||
[typeof(ulong)]) ?? | ||
throw new PlatformNotSupportedException("failed to get Rid constructor"); | ||
DynamicMethod dm = new("ConstructRid", typeof(Rid), [typeof(ulong)]); | ||
ILGenerator il = dm.GetILGenerator(); | ||
il.Emit(OpCodes.Ldarg_0); | ||
il.Emit(OpCodes.Newobj, cinfo); | ||
il.Emit(OpCodes.Ret); | ||
ConstructRid = dm.CreateDelegate<Func<ulong, Rid>>(); | ||
} | ||
#else | ||
public static Rid ConstructRid(ulong id) | ||
public static unsafe Rid ConstructRid(ulong id) | ||
{ | ||
Debug.Assert(Marshal.SizeOf<Rid>() == sizeof(ulong)); | ||
nint ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Rid>()); | ||
byte[] bytes = BitConverter.GetBytes(id); | ||
Marshal.Copy(bytes, 0, ptr, bytes.Length); | ||
Rid rv = Marshal.PtrToStructure<Rid>(ptr); | ||
Marshal.FreeHGlobal(ptr); | ||
Rid rv; | ||
Buffer.MemoryCopy(&id, &rv, sizeof(Rid), sizeof(ulong)); | ||
return rv; | ||
} | ||
#endif | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
using ImGuiNET; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace csbench; | ||
|
||
[MemoryDiagnoser] | ||
public class BenchCanvas | ||
{ | ||
private readonly ImDrawVertPtr v; | ||
private readonly Godot.Vector2[] points = new Godot.Vector2[1]; | ||
private readonly Godot.Color[] colors = new Godot.Color[1]; | ||
private readonly Godot.Vector2[] uvs = new Godot.Vector2[1]; | ||
|
||
public unsafe BenchCanvas() | ||
{ | ||
nint ptr = Marshal.AllocHGlobal(sizeof(ImDrawVert)); | ||
v = new(ptr) | ||
{ | ||
col = 0x12345678, | ||
uv = new(0.1f, 0.2f), | ||
pos = new(0.3f, 0.4f) | ||
}; | ||
} | ||
|
||
[Benchmark(Baseline = true)] | ||
public void ConvertVertex1() | ||
{ | ||
points[0] = new(v.pos.X, v.pos.Y); | ||
uint rgba = v.col; | ||
float r = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float g = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float b = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float a = (rgba & 0xFFu) / 255f; | ||
colors[0] = new(r, g, b, a); | ||
uvs[0] = new(v.uv.X, v.uv.Y); | ||
} | ||
|
||
// same as #1 | ||
[Benchmark] | ||
public void ConvertVertex2() | ||
{ | ||
ref var out_pos = ref points[0]; | ||
ref var out_color = ref colors[0]; | ||
ref var out_uv = ref uvs[0]; | ||
|
||
out_pos.X = v.pos.X; | ||
out_pos.Y = v.pos.Y; | ||
uint rgba = v.col; | ||
out_color.R = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.G = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.B = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.A = (rgba & 0xFFu) / 255f; | ||
out_uv.X = v.uv.X; | ||
out_uv.Y = v.uv.Y; | ||
} | ||
|
||
// this is a bit faster | ||
[Benchmark] | ||
public unsafe void ConvertVertex3() | ||
{ | ||
ref var out_pos = ref points[0]; | ||
ref var out_color = ref colors[0]; | ||
ref var out_uv = ref uvs[0]; | ||
|
||
ImDrawVert* p = v; | ||
|
||
out_pos.X = p->pos.X; | ||
out_pos.Y = p->pos.Y; | ||
uint rgba = p->col; | ||
out_color.R = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.G = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.B = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
out_color.A = (rgba & 0xFFu) / 255f; | ||
out_uv.X = p->uv.X; | ||
out_uv.Y = p->uv.Y; | ||
} | ||
|
||
// same as #3 | ||
[Benchmark] | ||
public unsafe void ConvertVertex4() | ||
{ | ||
ImDrawVert* p = v; | ||
|
||
points[0] = new(p->pos.X, p->pos.Y); | ||
uint rgba = p->col; | ||
float r = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float g = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float b = (rgba & 0xFFu) / 255f; | ||
rgba >>= 8; | ||
float a = (rgba & 0xFFu) / 255f; | ||
colors[0] = new(r, g, b, a); | ||
uvs[0] = new(p->uv.X, p->uv.Y); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
using Godot; | ||
using System.Reflection.Emit; | ||
using System.Reflection; | ||
using System.Runtime.InteropServices; | ||
using System.Runtime.CompilerServices; | ||
|
||
namespace csbench; | ||
|
||
[MemoryDiagnoser] | ||
public class BenchRid | ||
{ | ||
private readonly Func<ulong, Rid> _constructRid; | ||
private readonly ulong _id = 12345; | ||
private readonly nint _buf; | ||
|
||
public BenchRid() | ||
{ | ||
ConstructorInfo cinfo = typeof(Rid).GetConstructor( | ||
BindingFlags.NonPublic | BindingFlags.Instance, | ||
[typeof(ulong)]) ?? | ||
throw new PlatformNotSupportedException("failed to get Rid constructor"); | ||
DynamicMethod dm = new("ConstructRid", typeof(Rid), [typeof(ulong)]); | ||
ILGenerator il = dm.GetILGenerator(); | ||
il.Emit(OpCodes.Ldarg_0); | ||
il.Emit(OpCodes.Newobj, cinfo); | ||
il.Emit(OpCodes.Ret); | ||
_constructRid = dm.CreateDelegate<Func<ulong, Rid>>(); | ||
|
||
_buf = Marshal.AllocHGlobal(Marshal.SizeOf<Rid>()); | ||
} | ||
|
||
[Benchmark(Baseline = true)] | ||
public Rid ConstructRid_Emitted() | ||
{ | ||
return _constructRid(_id); | ||
} | ||
|
||
[Benchmark] | ||
public Rid ConstructRid_Marshal() | ||
{ | ||
nint ptr = Marshal.AllocHGlobal(Marshal.SizeOf<Rid>()); | ||
byte[] bytes = BitConverter.GetBytes(_id); | ||
Marshal.Copy(bytes, 0, ptr, bytes.Length); | ||
Rid rv = Marshal.PtrToStructure<Rid>(ptr); | ||
Marshal.FreeHGlobal(ptr); | ||
return rv; | ||
} | ||
|
||
[Benchmark] | ||
public Rid ConstructRid_Marshal_PreAlloc() | ||
{ | ||
// not thread-safe | ||
byte[] bytes = BitConverter.GetBytes(_id); | ||
Marshal.Copy(bytes, 0, _buf, bytes.Length); | ||
Rid rv = Marshal.PtrToStructure<Rid>(_buf); | ||
return rv; | ||
} | ||
|
||
[Benchmark] | ||
public unsafe Rid ConstructRid_Unsafe() | ||
{ | ||
Rid rv; | ||
byte[] bytes = BitConverter.GetBytes(_id); | ||
fixed (byte* pbytes = bytes) | ||
{ | ||
Buffer.MemoryCopy(pbytes, &rv, sizeof(Rid), bytes.Length); | ||
} | ||
return rv; | ||
} | ||
|
||
[Benchmark] | ||
public unsafe Rid ConstructRid_Unsafe_Direct() | ||
{ | ||
Rid rv; | ||
fixed (ulong* p = &_id) | ||
{ | ||
Buffer.MemoryCopy(p, &rv, sizeof(Rid), sizeof(ulong)); | ||
} | ||
return rv; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
using csbench; | ||
|
||
//BenchmarkRunner.Run<BenchCanvas>(); | ||
BenchmarkRunner.Run<BenchRid>(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net8.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks> | ||
<PublishAot>False</PublishAot> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" /> | ||
<PackageReference Include="GodotSharp" Version="4.3.0" /> | ||
<PackageReference Include="ImGui.NET" Version="1.91.0.1" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<Using Include="BenchmarkDotNet.Attributes" /> | ||
<Using Include="BenchmarkDotNet.Running" /> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.11.35222.181 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "csbench", "csbench.csproj", "{D37BA677-C36A-4F7A-9E87-72D5CF034A7B}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{D37BA677-C36A-4F7A-9E87-72D5CF034A7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{D37BA677-C36A-4F7A-9E87-72D5CF034A7B}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{D37BA677-C36A-4F7A-9E87-72D5CF034A7B}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{D37BA677-C36A-4F7A-9E87-72D5CF034A7B}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {21D11FF2-8E04-4538-B85D-36C1B5758C37} | ||
EndGlobalSection | ||
EndGlobal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters