diff --git a/src/Disruptor.Tests/Disruptor.Tests.csproj b/src/Disruptor.Tests/Disruptor.Tests.csproj index a4c630fa..db689ca3 100644 --- a/src/Disruptor.Tests/Disruptor.Tests.csproj +++ b/src/Disruptor.Tests/Disruptor.Tests.csproj @@ -5,6 +5,8 @@ + + diff --git a/src/Disruptor.Tests/FodyWeavers.xml b/src/Disruptor.Tests/FodyWeavers.xml new file mode 100644 index 00000000..bf03d886 --- /dev/null +++ b/src/Disruptor.Tests/FodyWeavers.xml @@ -0,0 +1,4 @@ + + + + diff --git a/src/Disruptor.Tests/Utils/UtilTests.cs b/src/Disruptor.Tests/Utils/UtilTests.cs index 761e529b..8812e582 100644 --- a/src/Disruptor.Tests/Utils/UtilTests.cs +++ b/src/Disruptor.Tests/Utils/UtilTests.cs @@ -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 { @@ -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(); + } } } diff --git a/src/Disruptor/Util/Util.cs b/src/Disruptor/Util/Util.cs index 28a65524..a42e68aa 100644 --- a/src/Disruptor/Util/Util.cs +++ b/src/Disruptor/Util/Util.cs @@ -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 returns the element @ address = array + array_data_offset + index x sizeof(object) - // = array + (index + 2) x sizeof(object) - // - // ReadValue 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(object array, int index) @@ -130,13 +135,13 @@ public static T Read(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 @@ -157,13 +162,10 @@ public static ref T ReadValue(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();