Skip to content

Commit

Permalink
Fix Util.Read methods for mono
Browse files Browse the repository at this point in the history
  • Loading branch information
ocoanet committed Feb 20, 2020
1 parent 7b51373 commit 382761e
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/Disruptor.Tests/Disruptor.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Fody" Version="3.2.4" PrivateAssets="all" />
<PackageReference Include="InlineIL.Fody" Version="0.7.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" />
Expand Down
4 changes: 4 additions & 0 deletions src/Disruptor.Tests/FodyWeavers.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8" ?>
<Weavers>
<InlineIL />
</Weavers>
33 changes: 33 additions & 0 deletions src/Disruptor.Tests/Utils/UtilTests.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using Disruptor.Tests.Support;
using InlineIL;
using NUnit.Framework;
using static InlineIL.IL.Emit;

namespace Disruptor.Tests.Utils
{
Expand Down Expand Up @@ -187,5 +190,35 @@ public UnalignedEvent(int value)
[FieldOffset(11)]
public int Value;
}

[Test]
public void ShouldGetArrayDataOffset()
{
Console.WriteLine(Environment.Is64BitProcess ? "64BIT" : "32BIT");

Assert.AreEqual(ComputeArrayDataOffset(), Util.ArrayDataOffset);
}

private static int ComputeArrayDataOffset()
{
var array = new object[1];

return (int)GetElementOffset(array, ref array[0]);
}

private static IntPtr GetElementOffset(object origin, ref object target)
{
IL.DeclareLocals(false, typeof(byte).MakeByRefType());

Ldarg(nameof(target));

Ldarg(nameof(origin)); // load the object
Stloc_0(); // convert the object pointer to a byref
Ldloc_0(); // load the object pointer as a byref

Sub();

return IL.Return<IntPtr>();
}
}
}
46 changes: 24 additions & 22 deletions src/Disruptor/Util/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,17 +107,22 @@ public static ISequence[] GetSequencesFor(params IEventProcessor[] processors)
return sequences;
}

//
// The offset from an array to the start of the array data is assumed to be 8 bytes for BIT32 and 16 bytes for BIT64 (= 2 x sizeof(object)).
// This offset was previously computed and stored in a static readonly field but in NetCore this field access introduces
// a method call that has a strong negative impact on performance.
//
// Read<T> returns the element @ address = array + array_data_offset + index x sizeof(object)
// = array + (index + 2) x sizeof(object)
//
// ReadValue<T> returns the element @ address = array + array_data_offset + index x sizeof(T)
// = array + sizeof(object) + sizeof(object) + index x sizeof(T)
//
// +----------+-----------------+--------------------+
// | Runtime | ArrayDataOffset | OffsetToStringData |
// +----------+-----------------+--------------------+
// | Core-x32 | 8 | 8 |
// | Core-x64 | 16 | 12 |
// | Mono-x32 | 16 | 12 |
// | Mono-x64 | 32 | 20 |
// +----------+-----------------+--------------------+

public static unsafe int ArrayDataOffset
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => sizeof(IntPtr) == 4
? RuntimeHelpers.OffsetToStringData == 8 ? 8 : 16
: RuntimeHelpers.OffsetToStringData == 12 ? 16 : 32;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T Read<T>(object array, int index)
Expand All @@ -130,13 +135,13 @@ public static T Read<T>(object array, int index)
Ldloc_0(); // load the object pointer as a byref

Ldarg(nameof(index));
Ldc_I4_2();
Add(); // index + 2

Sizeof(typeof(object));
Mul(); // (index + 2) x sizeof(object)
Mul(); // index x sizeof(object)

Call(MethodRef.PropertyGet(typeof(Util), nameof(ArrayDataOffset)));
Add(); // index x sizeof(object) + ArrayDataOffset

Add(); // array + (index + 2) x sizeof(object)
Add(); // array + index x sizeof(object) + ArrayDataOffset

Ldobj(typeof(T)); // load a T value from the computed address

Expand All @@ -157,13 +162,10 @@ public static ref T ReadValue<T>(object array, int index)
Sizeof(typeof(T));
Mul(); // index x sizeof(T)

Sizeof(typeof(object));
Add(); // index x sizeof(T) + sizeof(object)

Sizeof(typeof(object));
Add(); // index x sizeof(T) + sizeof(object) + sizeof(object)
Call(MethodRef.PropertyGet(typeof(Util), nameof(ArrayDataOffset)));
Add(); // index x sizeof(T) + ArrayDataOffset

Add(); // array + index x sizeof(T) + sizeof(object) + sizeof(object)
Add(); // array + index x sizeof(T) + ArrayDataOffset

Ret();

Expand Down

0 comments on commit 382761e

Please sign in to comment.