Skip to content

Commit dcda442

Browse files
committed
More micro optimizations
1 parent 1822f4f commit dcda442

4 files changed

Lines changed: 767 additions & 39 deletions

File tree

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
using System;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
using System.Runtime.Intrinsics;
5+
using BenchmarkDotNet.Attributes;
6+
7+
namespace Fusion.Execution.Benchmarks;
8+
9+
/// <summary>
10+
/// Compares row-write strategies for each MetaDb append pattern.
11+
/// All benchmarks write <see cref="Rows"/> rows of 20 bytes each to a
12+
/// pre-allocated buffer; we measure the per-row write cost only.
13+
///
14+
/// Patterns (mapped to real MetaDb methods):
15+
/// - AppendNull: 1 variable int + 4 zero ints
16+
/// - AppendEmptyProperty: 2 variable ints + 3 zero ints
17+
/// - AppendStartObject: 4 variable ints + 1 zero int
18+
/// </summary>
19+
internal static class DbRowBenchData
20+
{
21+
public const int RowSize = 20;
22+
public const int Rows = 4096;
23+
24+
public static byte[] Buffer { get; } = new byte[Rows * RowSize];
25+
public static int[] Parents { get; } = new int[Rows];
26+
public static int[] SelectionIds { get; } = new int[Rows];
27+
public static int[] PropertyCounts { get; } = new int[Rows];
28+
public static int[] Flags { get; } = new int[Rows];
29+
30+
static DbRowBenchData()
31+
{
32+
var rng = new Random(42);
33+
for (var i = 0; i < Rows; i++)
34+
{
35+
Parents[i] = rng.Next(0, 0x0FFFFFFF);
36+
SelectionIds[i] = rng.Next(0, 0x7FFF);
37+
PropertyCounts[i] = rng.Next(0, 32);
38+
Flags[i] = rng.Next(0, 63);
39+
}
40+
}
41+
42+
// ---------------- AppendNull ----------------
43+
44+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
45+
public static void WriteNull_FiveScalar(ref byte row, int parent)
46+
{
47+
Unsafe.WriteUnaligned(ref row, parent << 4);
48+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 4), 0);
49+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), 0);
50+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 12), 0);
51+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
52+
}
53+
54+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
55+
public static void WriteNull_ScalarPlusInitBlock(ref byte row, int parent)
56+
{
57+
Unsafe.WriteUnaligned(ref row, parent << 4);
58+
Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref row, 4), 0, 16);
59+
}
60+
61+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
62+
public static void WriteNull_Vec128PlusScalar(ref byte row, int parent)
63+
{
64+
// 16-byte zero-ish vector with int0 set, then trailing int zero.
65+
var v = Vector128.Create(parent << 4, 0, 0, 0).AsByte();
66+
v.StoreUnsafe(ref row);
67+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
68+
}
69+
70+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
71+
public static void WriteNull_ScalarPlusVec128Zero(ref byte row, int parent)
72+
{
73+
// Scalar write for int0, then a 16-byte Vector128 zero covering ints 1..4.
74+
Unsafe.WriteUnaligned(ref row, parent << 4);
75+
Vector128<byte>.Zero.StoreUnsafe(ref Unsafe.Add(ref row, 4));
76+
}
77+
78+
// ---------------- AppendEmptyProperty ----------------
79+
80+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
81+
public static void WriteEmptyProp_FiveScalar(ref byte row, int parent, int selectionId, int flags)
82+
{
83+
Unsafe.WriteUnaligned(ref row, 3 /*PropertyName*/ | (parent << 4));
84+
Unsafe.WriteUnaligned(
85+
ref Unsafe.Add(ref row, 4),
86+
selectionId | (2 << 15) | (flags << 17));
87+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), 0);
88+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 12), 0);
89+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
90+
}
91+
92+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
93+
public static void WriteEmptyProp_TwoScalarPlusInitBlock(ref byte row, int parent, int selectionId, int flags)
94+
{
95+
Unsafe.WriteUnaligned(ref row, 3 | (parent << 4));
96+
Unsafe.WriteUnaligned(
97+
ref Unsafe.Add(ref row, 4),
98+
selectionId | (2 << 15) | (flags << 17));
99+
Unsafe.InitBlockUnaligned(ref Unsafe.Add(ref row, 8), 0, 12);
100+
}
101+
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
public static void WriteEmptyProp_Vec128PlusScalar(ref byte row, int parent, int selectionId, int flags)
104+
{
105+
var int0 = 3 | (parent << 4);
106+
var int1 = selectionId | (2 << 15) | (flags << 17);
107+
108+
var v = Vector128.Create(int0, int1, 0, 0).AsByte();
109+
v.StoreUnsafe(ref row);
110+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
111+
}
112+
113+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
114+
public static void WriteEmptyProp_TwoScalarPlusVec128Zero(ref byte row, int parent, int selectionId, int flags)
115+
{
116+
Unsafe.WriteUnaligned(ref row, 3 | (parent << 4));
117+
Unsafe.WriteUnaligned(
118+
ref Unsafe.Add(ref row, 4),
119+
selectionId | (2 << 15) | (flags << 17));
120+
// Covers ints 2..4 using a 16-byte zero store (overwrites 4 bytes past end, but row is 20B
121+
// and buffer is row-aligned multiples of 20B — works only when room exists after).
122+
// To be safe here we do two stores: Vec128.Zero at offset 8 would write 16 bytes,
123+
// but only 12 are in our row. Fallback: 3 scalar zeros.
124+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), 0);
125+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 12), 0);
126+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
127+
}
128+
129+
// ---------------- AppendStartObject ----------------
130+
131+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
132+
public static void WriteStartObj_FiveScalar(ref byte row, int parent, int selectionId, int propertyCount, int flags)
133+
{
134+
Unsafe.WriteUnaligned(ref row, 1 | (parent << 4));
135+
Unsafe.WriteUnaligned(
136+
ref Unsafe.Add(ref row, 4),
137+
selectionId | (1 << 15) | (flags << 17));
138+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 8), propertyCount);
139+
Unsafe.WriteUnaligned(
140+
ref Unsafe.Add(ref row, 12),
141+
((propertyCount * 2) + 1) & 0x07FFFFFF);
142+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
143+
}
144+
145+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
146+
public static void WriteStartObj_Struct(ref byte row, int parent, int selectionId, int propertyCount, int flags)
147+
{
148+
var dbRow = new DbRowLocal(
149+
tokenType: 1,
150+
sizeOrLength: propertyCount,
151+
parentRow: parent,
152+
operationReferenceId: selectionId,
153+
operationReferenceType: 1,
154+
numberOfRows: (propertyCount * 2) + 1,
155+
flags: flags);
156+
Unsafe.WriteUnaligned(ref row, dbRow);
157+
}
158+
159+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
160+
public static void WriteStartObj_Vec128PlusScalar(ref byte row, int parent, int selectionId, int propertyCount, int flags)
161+
{
162+
var int0 = 1 | (parent << 4);
163+
var int1 = selectionId | (1 << 15) | (flags << 17);
164+
var int2 = propertyCount;
165+
var int3 = ((propertyCount * 2) + 1) & 0x07FFFFFF;
166+
167+
var v = Vector128.Create(int0, int1, int2, int3).AsByte();
168+
v.StoreUnsafe(ref row);
169+
Unsafe.WriteUnaligned(ref Unsafe.Add(ref row, 16), 0);
170+
}
171+
172+
[StructLayout(LayoutKind.Sequential)]
173+
public readonly struct DbRowLocal
174+
{
175+
private readonly int _typeAndParent;
176+
private readonly int _selectionAndFlags;
177+
private readonly int _sizeOrLengthUnion;
178+
private readonly int _locationOrRows;
179+
private readonly int _source;
180+
181+
public DbRowLocal(
182+
int tokenType,
183+
int location = 0,
184+
int sizeOrLength = 0,
185+
int sourceDocumentId = 0,
186+
int parentRow = 0,
187+
int operationReferenceId = 0,
188+
int operationReferenceType = 0,
189+
int numberOfRows = 0,
190+
int flags = 0)
191+
{
192+
var locationOrRows = location != 0 ? location : numberOfRows;
193+
_typeAndParent = (tokenType & 0x0F) | (parentRow << 4);
194+
_selectionAndFlags = operationReferenceId
195+
| (operationReferenceType << 15)
196+
| (flags << 17);
197+
_sizeOrLengthUnion = sizeOrLength;
198+
_locationOrRows = locationOrRows & 0x07FFFFFF;
199+
_source = sourceDocumentId & 0x7FFF;
200+
}
201+
}
202+
}
203+
204+
[MemoryDiagnoser]
205+
[InProcess]
206+
public class AppendNullWriteBenchmark
207+
{
208+
[Benchmark(Baseline = true)]
209+
public void FiveScalar()
210+
{
211+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
212+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
213+
214+
for (var i = 0; i < DbRowBenchData.Rows; i++)
215+
{
216+
DbRowBenchData.WriteNull_FiveScalar(
217+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
218+
Unsafe.Add(ref parents, i));
219+
}
220+
}
221+
222+
[Benchmark]
223+
public void ScalarPlusInitBlock()
224+
{
225+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
226+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
227+
228+
for (var i = 0; i < DbRowBenchData.Rows; i++)
229+
{
230+
DbRowBenchData.WriteNull_ScalarPlusInitBlock(
231+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
232+
Unsafe.Add(ref parents, i));
233+
}
234+
}
235+
236+
[Benchmark]
237+
public void Vec128PlusScalar()
238+
{
239+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
240+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
241+
242+
for (var i = 0; i < DbRowBenchData.Rows; i++)
243+
{
244+
DbRowBenchData.WriteNull_Vec128PlusScalar(
245+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
246+
Unsafe.Add(ref parents, i));
247+
}
248+
}
249+
250+
[Benchmark]
251+
public void ScalarPlusVec128Zero()
252+
{
253+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
254+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
255+
256+
for (var i = 0; i < DbRowBenchData.Rows; i++)
257+
{
258+
DbRowBenchData.WriteNull_ScalarPlusVec128Zero(
259+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
260+
Unsafe.Add(ref parents, i));
261+
}
262+
}
263+
}
264+
265+
[MemoryDiagnoser]
266+
[InProcess]
267+
public class AppendEmptyPropertyWriteBenchmark
268+
{
269+
[Benchmark(Baseline = true)]
270+
public void FiveScalar()
271+
{
272+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
273+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
274+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
275+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
276+
277+
for (var i = 0; i < DbRowBenchData.Rows; i++)
278+
{
279+
DbRowBenchData.WriteEmptyProp_FiveScalar(
280+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
281+
Unsafe.Add(ref parents, i),
282+
Unsafe.Add(ref selIds, i),
283+
Unsafe.Add(ref flags, i));
284+
}
285+
}
286+
287+
[Benchmark]
288+
public void TwoScalarPlusInitBlock()
289+
{
290+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
291+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
292+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
293+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
294+
295+
for (var i = 0; i < DbRowBenchData.Rows; i++)
296+
{
297+
DbRowBenchData.WriteEmptyProp_TwoScalarPlusInitBlock(
298+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
299+
Unsafe.Add(ref parents, i),
300+
Unsafe.Add(ref selIds, i),
301+
Unsafe.Add(ref flags, i));
302+
}
303+
}
304+
305+
[Benchmark]
306+
public void Vec128PlusScalar()
307+
{
308+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
309+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
310+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
311+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
312+
313+
for (var i = 0; i < DbRowBenchData.Rows; i++)
314+
{
315+
DbRowBenchData.WriteEmptyProp_Vec128PlusScalar(
316+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
317+
Unsafe.Add(ref parents, i),
318+
Unsafe.Add(ref selIds, i),
319+
Unsafe.Add(ref flags, i));
320+
}
321+
}
322+
}
323+
324+
[MemoryDiagnoser]
325+
[InProcess]
326+
public class AppendStartObjectWriteBenchmark
327+
{
328+
[Benchmark(Baseline = true)]
329+
public void FiveScalar()
330+
{
331+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
332+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
333+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
334+
ref var counts = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.PropertyCounts);
335+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
336+
337+
for (var i = 0; i < DbRowBenchData.Rows; i++)
338+
{
339+
DbRowBenchData.WriteStartObj_FiveScalar(
340+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
341+
Unsafe.Add(ref parents, i),
342+
Unsafe.Add(ref selIds, i),
343+
Unsafe.Add(ref counts, i),
344+
Unsafe.Add(ref flags, i));
345+
}
346+
}
347+
348+
[Benchmark]
349+
public void DbRowStruct()
350+
{
351+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
352+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
353+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
354+
ref var counts = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.PropertyCounts);
355+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
356+
357+
for (var i = 0; i < DbRowBenchData.Rows; i++)
358+
{
359+
DbRowBenchData.WriteStartObj_Struct(
360+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
361+
Unsafe.Add(ref parents, i),
362+
Unsafe.Add(ref selIds, i),
363+
Unsafe.Add(ref counts, i),
364+
Unsafe.Add(ref flags, i));
365+
}
366+
}
367+
368+
[Benchmark]
369+
public void Vec128PlusScalar()
370+
{
371+
ref var dest = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Buffer);
372+
ref var parents = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Parents);
373+
ref var selIds = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.SelectionIds);
374+
ref var counts = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.PropertyCounts);
375+
ref var flags = ref MemoryMarshal.GetArrayDataReference(DbRowBenchData.Flags);
376+
377+
for (var i = 0; i < DbRowBenchData.Rows; i++)
378+
{
379+
DbRowBenchData.WriteStartObj_Vec128PlusScalar(
380+
ref Unsafe.Add(ref dest, i * DbRowBenchData.RowSize),
381+
Unsafe.Add(ref parents, i),
382+
Unsafe.Add(ref selIds, i),
383+
Unsafe.Add(ref counts, i),
384+
Unsafe.Add(ref flags, i));
385+
}
386+
}
387+
}

0 commit comments

Comments
 (0)