Skip to content

Commit ce72d51

Browse files
authored
perf: soft revert of the performance regressions introduced in #1469 (#2885)
1 parent 6f2e055 commit ce72d51

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+231
-158
lines changed

sources/core/Stride.Core.Serialization/Serialization/LZ4/LZ4Stream.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ public override unsafe int Read(byte[] buffer, int offset, int count)
380380
fixed (byte* pDst = buffer)
381381
{
382382
Debug.Assert(pSrc is not null);
383-
Unsafe.CopyBlockUnaligned(pDst + offset, pSrc + bufferOffset, (uint)chunk);
383+
Utilities.CopyWithAlignmentFallback(pDst + offset, pSrc + bufferOffset, (uint)chunk);
384384
}
385385
}
386386

@@ -511,7 +511,7 @@ public override unsafe void Write(byte[] buffer, int offset, int count)
511511
fixed (byte* pDst = dataBuffer)
512512
{
513513
Debug.Assert(pDst is not null);
514-
Unsafe.CopyBlockUnaligned(pDst + bufferOffset, pSrc + offset, (uint)chunk);
514+
Utilities.CopyWithAlignmentFallback(pDst + bufferOffset, pSrc + offset, (uint)chunk);
515515
}
516516
}
517517

sources/core/Stride.Core.Serialization/Storage/Blob.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ internal unsafe Blob(ObjectDatabase objectDatabase, ObjectId objectId, IntPtr co
2424
Debug.Assert(size >= 0);
2525
this.Size = size;
2626
this.Content = Marshal.AllocHGlobal(size);
27-
Unsafe.CopyBlockUnaligned((void*)this.Content, (void*)content, (uint)size);
27+
Utilities.CopyWithAlignmentFallback((void*)this.Content, (void*)content, (uint)size);
2828
}
2929

3030
internal unsafe Blob(ObjectDatabase objectDatabase, ObjectId objectId, Stream stream)

sources/core/Stride.Core.Serialization/Streaming/ContentChunk.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ public unsafe IntPtr GetData(DatabaseFileProvider fileProvider)
104104
var read = (uint)stream.Read(buffer, 0, (int)Math.Min(count, bufferCapacity));
105105
if (read <= 0)
106106
break;
107-
Unsafe.CopyBlockUnaligned(chunkBytesPtr, bufferStart, read);
107+
Utilities.CopyWithAlignmentFallback(chunkBytesPtr, bufferStart, read);
108108
chunkBytesPtr += read;
109109
count -= read;
110110
} while (count > 0);

sources/core/Stride.Core/UnmanagedArray.cs

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,13 @@ namespace Stride.Core;
88
[Obsolete("Obtain Memory<T> using GC.Allocate*Array or a Stride-specific allocator mechanism.")]
99
public class UnmanagedArray<T> : IDisposable where T : struct
1010
{
11-
private readonly int sizeOfT;
1211
private readonly bool isShared;
1312

1413
[Obsolete("Obtain Memory<T> using GC.Allocate*Array or a Stride-specific allocator mechanism.")]
1514
public UnmanagedArray(int length)
1615
{
1716
Length = length;
18-
sizeOfT = Unsafe.SizeOf<T>();
19-
var finalSize = length * sizeOfT;
17+
var finalSize = length * Unsafe.SizeOf<T>();
2018
Pointer = Utilities.AllocateMemory(finalSize);
2119
isShared = false;
2220
}
@@ -43,8 +41,10 @@ public T this[int index]
4341
unsafe
4442
{
4543
var bptr = (byte*)Pointer;
46-
bptr += index * sizeOfT;
47-
res = Unsafe.ReadUnaligned<T>(bptr);
44+
bptr += index * Unsafe.SizeOf<T>();
45+
// Pointer is aligned, we expect the struct to be aligned as well;
46+
// If the user decides to Pack=1, this scope is the least of their worries
47+
res = Unsafe.Read<T>(bptr);
4848
}
4949

5050
return res;
@@ -60,8 +60,10 @@ public T this[int index]
6060
unsafe
6161
{
6262
var bptr = (byte*)Pointer;
63-
bptr += index * sizeOfT;
64-
Unsafe.WriteUnaligned(bptr, value);
63+
bptr += index * Unsafe.SizeOf<T>();
64+
// Pointer is aligned, we expect the struct to be aligned as well;
65+
// If the user decides to Pack=1, this scope is the least of their worries
66+
Unsafe.Write(bptr, value);
6567
}
6668
}
6769
}
@@ -72,7 +74,6 @@ public unsafe void Read(T[] destination, int offset = 0)
7274
{
7375
throw new ArgumentOutOfRangeException(nameof(offset));
7476
}
75-
// Interop.Read((void*)Pointer, destination, offset, destination.Length);
7677
new Span<T>((void*)Pointer, destination.Length - offset).CopyTo(destination.AsSpan(offset));
7778
}
7879

@@ -87,8 +88,7 @@ public void Read(T[] destination, int pointerByteOffset, int arrayOffset, int ar
8788
{
8889
var ptr = (byte*)Pointer;
8990
ptr += pointerByteOffset;
90-
// Interop.Read(ptr, destination, arrayOffset, arrayLen);
91-
new Span<T>(ptr, sizeOfT * arrayLen)
91+
new Span<T>(ptr, Unsafe.SizeOf<T>() * arrayLen)
9292
.CopyTo(destination.AsSpan(arrayOffset, arrayLen));
9393
}
9494
}
@@ -99,7 +99,6 @@ public unsafe void Write(T[] source, int offset = 0)
9999
{
100100
throw new ArgumentOutOfRangeException();
101101
}
102-
// Interop.Write((void*)Pointer, source, offset, source.Length);
103102
source.AsSpan(offset).CopyTo(new Span<T>((void*)Pointer, source.Length - offset));
104103
}
105104

@@ -112,7 +111,6 @@ public unsafe void Write(T[] source, int pointerByteOffset, int arrayOffset, int
112111

113112
var ptr = (byte*)Pointer;
114113
ptr += pointerByteOffset;
115-
// Interop.Write(ptr, source, arrayOffset, arrayLen);
116114
source.AsSpan(arrayOffset, arrayLen).CopyTo(new Span<T>(ptr, arrayLen));
117115
}
118116

sources/core/Stride.Core/Utilities.cs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,50 @@ namespace Stride.Core;
3434
/// </summary>
3535
public static class Utilities
3636
{
37+
// MUST BE A METHOD AND AGGRESSIVELY INLINED, OTHERWISE THE JIT WILL NOT ELIMINATE THE BRANCH
38+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
39+
private static bool IsUnalignedSafe() =>
40+
RuntimeInformation.ProcessArchitecture is Architecture.X64 or Architecture.X86 or Architecture.Arm64;
41+
42+
/// <inheritdoc cref="AlignmentFallbackDoc"/>
43+
/// <inheritdoc cref="Unsafe.CopyBlock(ref byte, ref readonly byte, uint)"/>
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
public static unsafe void CopyWithAlignmentFallback(ref byte destination, ref readonly byte source, uint byteCount)
46+
{
47+
if (IsUnalignedSafe())
48+
fixed (void* src = &source, dst = &destination)
49+
Buffer.MemoryCopy(src, dst, byteCount, byteCount);
50+
else
51+
Unsafe.CopyBlockUnaligned(ref destination, in source, byteCount);
52+
}
53+
54+
/// <inheritdoc cref="AlignmentFallbackDoc"/>
55+
/// <inheritdoc cref="Unsafe.CopyBlock(ref byte, ref readonly byte, uint)"/>
56+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
57+
public static unsafe void CopyWithAlignmentFallback(void* destination, void* source, uint byteCount)
58+
{
59+
if (IsUnalignedSafe())
60+
Buffer.MemoryCopy(source, destination, byteCount, byteCount);
61+
else
62+
Unsafe.CopyBlockUnaligned(destination, source, byteCount);
63+
}
64+
65+
/// <summary>
66+
/// Zero out memory at the address provided
67+
/// </summary>
68+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
69+
public static unsafe void Clear(void* startAddress, uint byteCount)
70+
{
71+
// Span swaps between InitBlockUnaligned and _ZeroMemory depending on the size
72+
new Span<byte>(startAddress, (int)byteCount).Clear();
73+
}
74+
75+
/// <remarks>
76+
/// Some of the architecture dotnet runs on do not support arbitrary unaligned reads or writes,
77+
/// use this instead of other memcopy if you aren't sure whether the pointers you passed in are aligned.
78+
/// </remarks>
79+
static void AlignmentFallbackDoc() { }
80+
3781
/// <summary>
3882
/// Allocate an aligned memory buffer.
3983
/// </summary>

sources/engine/Stride.Engine/Animations/AnimationBlender.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,15 @@ public static unsafe void Blend(CoreAnimationOperation blendOperation, float ble
179179
if (factorLeft > 0.0f && factorRight == 0.0f)
180180
{
181181
*resultData++ = 1.0f;
182-
Unsafe.CopyBlockUnaligned(resultData, sourceLeftData, (uint)channel.Size);
182+
Utilities.CopyWithAlignmentFallback(resultData, sourceLeftData, (uint)channel.Size);
183183
continue;
184184
}
185185

186186
// Use right value
187187
if (factorRight > 0.0f && factorLeft == 0.0f)
188188
{
189189
*resultData++ = 1.0f;
190-
Unsafe.CopyBlockUnaligned(resultData, sourceRightData, (uint)channel.Size);
190+
Utilities.CopyWithAlignmentFallback(resultData, sourceRightData, (uint)channel.Size);
191191
continue;
192192
}
193193

@@ -202,7 +202,7 @@ public static unsafe void Blend(CoreAnimationOperation blendOperation, float ble
202202
switch (channel.BlendType)
203203
{
204204
case BlendType.Blit:
205-
Unsafe.CopyBlockUnaligned(
205+
Utilities.CopyWithAlignmentFallback(
206206
resultData,
207207
blendFactor < 0.5f ? sourceLeftData : sourceRightData,
208208
(uint)channel.Size);
@@ -229,7 +229,7 @@ public static unsafe void Blend(CoreAnimationOperation blendOperation, float ble
229229
switch (channel.BlendType)
230230
{
231231
case BlendType.Blit:
232-
Unsafe.CopyBlockUnaligned(resultData, sourceLeftData, (uint)channel.Size);
232+
Utilities.CopyWithAlignmentFallback(resultData, sourceLeftData, (uint)channel.Size);
233233
break;
234234
case BlendType.Float2:
235235
Vector2 rightValue2;
@@ -257,7 +257,7 @@ public static unsafe void Blend(CoreAnimationOperation blendOperation, float ble
257257
switch (channel.BlendType)
258258
{
259259
case BlendType.Blit:
260-
Unsafe.CopyBlockUnaligned(resultData, sourceLeftData, (uint)channel.Size);
260+
Utilities.CopyWithAlignmentFallback(resultData, sourceLeftData, (uint)channel.Size);
261261
break;
262262
case BlendType.Float2:
263263
Vector2 rightValue2;

sources/engine/Stride.Engine/Animations/AnimationClipEvaluator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public unsafe void AddCurveValues(CompressedTimeSpan newTime, AnimationClipResul
114114
var channel = Channels.Items[index];
115115

116116
// For now, objects are not supported, so treat everything as a blittable struct.
117-
channel.Curve?.AddValue(newTime, (IntPtr)(structures + channel.Offset + sizeof(float)));
117+
channel.Curve?.AddValue(newTime, (structures + channel.Offset + sizeof(float)));
118118
}
119119
}
120120
}

sources/engine/Stride.Engine/Animations/AnimationCurve.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public abstract class AnimationCurve
5050
/// </summary>
5151
/// <param name="newTime">The new time.</param>
5252
/// <param name="location">The location.</param>
53-
public abstract void AddValue(CompressedTimeSpan newTime, IntPtr location);
53+
public abstract unsafe void AddValue(CompressedTimeSpan newTime, byte* location);
5454

5555
/// <summary>
5656
/// Meant for internal use, to call AnimationData{T}.FromAnimationChannels() without knowing the generic type.
@@ -142,9 +142,9 @@ public int FindKeyIndex(CompressedTimeSpan time)
142142
}
143143

144144
/// <inheritdoc/>
145-
public override unsafe void AddValue(CompressedTimeSpan newTime, nint location)
145+
public override unsafe void AddValue(CompressedTimeSpan newTime, byte* location)
146146
{
147-
var value = Unsafe.ReadUnaligned<T>((void*)location);
147+
var value = Unsafe.ReadUnaligned<T>(location);
148148
KeyFrames.Add(new(newTime, value));
149149
}
150150

sources/engine/Stride.Engine/Updater/UpdatableField.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ ldarg data
7373
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7474
public unsafe void SetBlittable(IntPtr obj, IntPtr data)
7575
{
76-
Unsafe.CopyBlockUnaligned((void*)obj, (void*)data, (uint)Size);
76+
Utilities.CopyWithAlignmentFallback((void*)obj, (void*)data, (uint)Size);
7777
}
7878

7979
/// <summary>

sources/engine/Stride.Graphics/Buffer.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ public unsafe void GetData<T>(CommandList commandList, Buffer stagingTexture, Sp
326326
// Map the staging resource to a CPU accessible memory
327327
var mappedResource = commandList.MapSubresource(stagingTexture, 0, MapMode.Read);
328328
fixed (void* pointer = toData)
329-
Unsafe.CopyBlockUnaligned(pointer, (void*)mappedResource.DataBox.DataPointer, (uint)toDataInBytes);
329+
Utilities.CopyWithAlignmentFallback(pointer, (void*)mappedResource.DataBox.DataPointer, (uint)toDataInBytes);
330330
// Make sure that we unmap the resource in case of an exception
331331
commandList.UnmapSubresource(mappedResource);
332332
}
@@ -386,7 +386,7 @@ public unsafe void SetData<T>(CommandList commandList, ReadOnlySpan<T> fromData,
386386
throw new ArgumentException("offset is only supported for textured declared with ResourceUsage.Default", "offsetInBytes");
387387

388388
var mappedResource = commandList.MapSubresource(this, 0, Usage == GraphicsResourceUsage.Staging ? MapMode.Write : MapMode.WriteDiscard);
389-
Unsafe.CopyBlockUnaligned((void*)mappedResource.DataBox.DataPointer, pointer, (uint)sizeInBytes);
389+
Utilities.CopyWithAlignmentFallback((void*)mappedResource.DataBox.DataPointer, pointer, (uint)sizeInBytes);
390390
commandList.UnmapSubresource(mappedResource);
391391
}
392392
}

0 commit comments

Comments
 (0)