Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormikedn <onemihaid@hotmail.com>2018-03-01 18:34:12 +0300
committerStephen Toub <stoub@microsoft.com>2018-03-02 01:14:55 +0300
commitb5fd1dae2c1268839ff624a3aa34029c43d6f42b (patch)
treefdc88f00613bbf828ca2d921cbe8de7788e5e619
parentd270a3d8f2d70adc5e22a87cbfe8ec3e5ceb8a64 (diff)
Improve MemoryMarshal.Cast (dotnet/coreclr#16659)
Avoid unnecessary checked and signed arithmetic. Handle special cases such as cast between same size types and from byte sized types, the JIT is unable to optimize these cases currently. Signed-off-by: dotnet-bot <dotnet-bot@microsoft.com>
-rw-r--r--src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs62
1 files changed, 60 insertions, 2 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
index 967459e4b..8a5bb7539 100644
--- a/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
+++ b/src/System.Private.CoreLib/shared/System/Runtime/InteropServices/MemoryMarshal.Fast.cs
@@ -59,9 +59,38 @@ namespace System.Runtime.InteropServices
if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
+ // Use unsigned integers - unsigned division by constant (especially by power of 2)
+ // and checked casts are faster and smaller.
+ uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
+ uint toSize = (uint)Unsafe.SizeOf<TTo>();
+ uint fromLength = (uint)source.Length;
+ int toLength;
+ if (fromSize == toSize)
+ {
+ // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // should be optimized to just `length` but the JIT doesn't do that today.
+ toLength = (int)fromLength;
+ }
+ else if (fromSize == 1)
+ {
+ // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
+ // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
+ // the JIT can't eliminate long multiply by 1.
+ toLength = (int)(fromLength / toSize);
+ }
+ else
+ {
+ // Ensure that casts are done in such a way that the JIT is able to "see"
+ // the uint->ulong casts and the multiply together so that on 32 bit targets
+ // 32x32to64 multiplication is used.
+ ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
+ toLength = checked((int)toLengthUInt64);
+ }
+
return new Span<TTo>(
ref Unsafe.As<TFrom, TTo>(ref source._pointer.Value),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ toLength);
}
/// <summary>
@@ -84,9 +113,38 @@ namespace System.Runtime.InteropServices
if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
+ // Use unsigned integers - unsigned division by constant (especially by power of 2)
+ // and checked casts are faster and smaller.
+ uint fromSize = (uint)Unsafe.SizeOf<TFrom>();
+ uint toSize = (uint)Unsafe.SizeOf<TTo>();
+ uint fromLength = (uint)source.Length;
+ int toLength;
+ if (fromSize == toSize)
+ {
+ // Special case for same size types - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // should be optimized to just `length` but the JIT doesn't do that today.
+ toLength = (int)fromLength;
+ }
+ else if (fromSize == 1)
+ {
+ // Special case for byte sized TFrom - `(ulong)fromLength * (ulong)fromSize / (ulong)toSize`
+ // becomes `(ulong)fromLength / (ulong)toSize` but the JIT can't narrow it down to `int`
+ // and can't eliminate the checked cast. This also avoids a 32 bit specific issue,
+ // the JIT can't eliminate long multiply by 1.
+ toLength = (int)(fromLength / toSize);
+ }
+ else
+ {
+ // Ensure that casts are done in such a way that the JIT is able to "see"
+ // the uint->ulong casts and the multiply together so that on 32 bit targets
+ // 32x32to64 multiplication is used.
+ ulong toLengthUInt64 = (ulong)fromLength * (ulong)fromSize / (ulong)toSize;
+ toLength = checked((int)toLengthUInt64);
+ }
+
return new ReadOnlySpan<TTo>(
ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)),
- checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
+ toLength);
}
/// <summary>