com.wallstop-studios.unity-helpers 2.1.1 → 2.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/Docs/ILIST_SORTING_PERFORMANCE.md +16 -16
- package/Docs/INDEX.md +1 -0
- package/Docs/RANDOM_PERFORMANCE.md +15 -15
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/{package-lock.json.meta → Docs/REFLECTION_PERFORMANCE.md.meta} +1 -1
- package/Docs/RELATIONAL_COMPONENTS.md +6 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md +63 -0
- package/Docs/RELATIONAL_COMPONENT_PERFORMANCE.md.meta +7 -0
- package/Docs/SPATIAL_TREE_2D_PERFORMANCE.md +64 -64
- package/Docs/SPATIAL_TREE_3D_PERFORMANCE.md +64 -64
- package/Editor/Sprites/AnimationCopier.cs +1 -1
- package/Editor/Sprites/AnimationViewerWindow.cs +4 -4
- package/Editor/Sprites/SpriteSettingsApplierAPI.cs +2 -1
- package/Editor/Sprites/TextureResizerWizard.cs +4 -3
- package/Editor/Utils/ScriptableObjectSingletonCreator.cs +3 -3
- package/README.md +8 -3
- package/Runtime/Core/Attributes/BaseRelationalComponentAttribute.cs +147 -20
- package/Runtime/Core/Attributes/ChildComponentAttribute.cs +630 -117
- package/Runtime/Core/Attributes/NotNullAttribute.cs +5 -2
- package/Runtime/Core/Attributes/ParentComponentAttribute.cs +477 -103
- package/Runtime/Core/Attributes/RelationalComponentAssigner.cs +26 -3
- package/Runtime/Core/Attributes/RelationalComponentExtensions.cs +19 -3
- package/Runtime/Core/Attributes/SiblingComponentAttribute.cs +265 -92
- package/Runtime/Core/CodeGen.meta +8 -0
- package/Runtime/Core/DataStructure/ImmutableBitSet.cs +5 -20
- package/Runtime/Core/Helper/Helpers.cs +8 -0
- package/Runtime/Core/Helper/Logging/UnityLogTagFormatter.cs +11 -7
- package/Runtime/Core/Helper/Objects.cs +1 -1
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs +5142 -0
- package/Runtime/Core/Helper/ReflectionHelpers.Factory.cs.meta +11 -0
- package/Runtime/Core/Helper/ReflectionHelpers.cs +1812 -1518
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/FlurryBurstRandom.cs +0 -6
- package/Runtime/Tags/AttributeMetadataCache.cs +4 -6
- package/Runtime/Tags/CosmeticEffectData.cs +1 -1
- package/Runtime/Visuals/UIToolkit/MultiFileSelectorElement.cs +3 -3
- package/Tests/Editor/Helper/HelpersTests.cs +2 -2
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs +87 -0
- package/Tests/Editor/Helper/ReflectionHelpersTypedEditorTests.cs.meta +11 -0
- package/Tests/Editor/Helper/SpriteHelpersTests.cs +1 -1
- package/Tests/Editor/PrefabCheckerReportTests.cs +3 -3
- package/Tests/Editor/Sprites/AnimationCopierFilterTests.cs +18 -12
- package/Tests/Editor/Sprites/AnimationCopierWindowTests.cs +8 -7
- package/Tests/Editor/Sprites/AnimationViewerWindowTests.cs +2 -1
- package/Tests/Editor/Sprites/ScriptableSpriteAtlasEditorTests.cs +6 -5
- package/Tests/Editor/Sprites/SpriteCropperAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpriteCropperTests.cs +7 -6
- package/Tests/Editor/Sprites/SpritePivotAdjusterAdditionalTests.cs +2 -1
- package/Tests/Editor/Sprites/SpritePivotAdjusterTests.cs +4 -3
- package/Tests/Editor/Sprites/TextureResizerWizardTests.cs +10 -9
- package/Tests/Editor/Sprites/TextureSettingsApplierAPITests.cs +2 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +1 -1
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs +2923 -0
- package/Tests/Runtime/Helper/ReflectionHelperCapabilityMatrixTests.cs.meta +11 -0
- package/Tests/Runtime/Helper/ReflectionHelperTests.cs +660 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs +1238 -0
- package/Tests/Runtime/Performance/ReflectionPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs +832 -0
- package/Tests/Runtime/Performance/RelationalComponentBenchmarkTests.cs.meta +11 -0
- package/package.json +1 -1
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs +0 -60
- package/Tests/Runtime/Performance/RelationComponentPerformanceTests.cs.meta +0 -3
|
@@ -0,0 +1,832 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Tests.Performance
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Diagnostics;
|
|
6
|
+
using System.Globalization;
|
|
7
|
+
using NUnit.Framework;
|
|
8
|
+
using UnityEngine;
|
|
9
|
+
using WallstopStudios.UnityHelpers.Core.Attributes;
|
|
10
|
+
|
|
11
|
+
public sealed class RelationalComponentBenchmarkTests
|
|
12
|
+
{
|
|
13
|
+
private const int NumIterations = 10_000;
|
|
14
|
+
|
|
15
|
+
private const string DocumentPath = "Docs/RELATIONAL_COMPONENT_PERFORMANCE.md";
|
|
16
|
+
private const string SectionPrefix = "RELATIONAL_COMPONENTS_";
|
|
17
|
+
|
|
18
|
+
private static readonly TimeSpan BenchmarkDuration = TimeSpan.FromSeconds(1);
|
|
19
|
+
|
|
20
|
+
private static readonly Func<ScenarioResult>[] ScenarioFactories =
|
|
21
|
+
{
|
|
22
|
+
RunParentSingleScenario,
|
|
23
|
+
RunParentArrayScenario,
|
|
24
|
+
RunParentListScenario,
|
|
25
|
+
RunParentHashSetScenario,
|
|
26
|
+
RunChildSingleScenario,
|
|
27
|
+
RunChildArrayScenario,
|
|
28
|
+
RunChildListScenario,
|
|
29
|
+
RunChildHashSetScenario,
|
|
30
|
+
RunSiblingSingleScenario,
|
|
31
|
+
RunSiblingArrayScenario,
|
|
32
|
+
RunSiblingListScenario,
|
|
33
|
+
RunSiblingHashSetScenario,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
[Test]
|
|
37
|
+
[Timeout(0)]
|
|
38
|
+
public void Benchmark()
|
|
39
|
+
{
|
|
40
|
+
List<ScenarioResult> results = new();
|
|
41
|
+
foreach (Func<ScenarioResult> factory in ScenarioFactories)
|
|
42
|
+
{
|
|
43
|
+
ScenarioResult result = factory();
|
|
44
|
+
results.Add(result);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
string opsHeader =
|
|
48
|
+
"| Scenario | Relational Ops/s | Manual Ops/s | Rel/Manual | Iterations |";
|
|
49
|
+
string opsDivider = "| --- | ---: | ---: | ---: | ---: |";
|
|
50
|
+
|
|
51
|
+
UnityEngine.Debug.Log("### Relational Component Assignment Benchmarks");
|
|
52
|
+
UnityEngine.Debug.Log(opsHeader);
|
|
53
|
+
UnityEngine.Debug.Log(opsDivider);
|
|
54
|
+
|
|
55
|
+
List<string> sectionLines = new()
|
|
56
|
+
{
|
|
57
|
+
string.Format(
|
|
58
|
+
CultureInfo.InvariantCulture,
|
|
59
|
+
"_Last updated {0:yyyy-MM-dd HH:mm} UTC on {1}_",
|
|
60
|
+
DateTime.UtcNow,
|
|
61
|
+
SystemInfo.operatingSystem
|
|
62
|
+
),
|
|
63
|
+
string.Empty,
|
|
64
|
+
"Numbers capture repeated `Assign*Components` calls for one second per scenario.",
|
|
65
|
+
"Higher operations per second are better.",
|
|
66
|
+
string.Empty,
|
|
67
|
+
"### Operations per second (higher is better)",
|
|
68
|
+
opsHeader,
|
|
69
|
+
opsDivider,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
foreach (ScenarioResult result in results)
|
|
73
|
+
{
|
|
74
|
+
string opsRow = FormatOpsRow(result);
|
|
75
|
+
UnityEngine.Debug.Log(opsRow);
|
|
76
|
+
sectionLines.Add(opsRow);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sectionLines.Add(string.Empty);
|
|
80
|
+
|
|
81
|
+
string sectionName = SectionPrefix + GetOperatingSystemToken();
|
|
82
|
+
BenchmarkReadmeUpdater.UpdateSection(sectionName, sectionLines, DocumentPath);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private static ScenarioResult RunParentSingleScenario()
|
|
86
|
+
{
|
|
87
|
+
GameObject root = CreateGameObject("ParentSingleRoot");
|
|
88
|
+
root.AddComponent<BoxCollider>();
|
|
89
|
+
|
|
90
|
+
GameObject child = CreateGameObject("ParentSingleChild");
|
|
91
|
+
child.transform.SetParent(root.transform, false);
|
|
92
|
+
|
|
93
|
+
ParentSingleRelational relational = child.AddComponent<ParentSingleRelational>();
|
|
94
|
+
ParentSingleManual manual = child.AddComponent<ParentSingleManual>();
|
|
95
|
+
|
|
96
|
+
ScenarioResult result = ExecuteScenario(
|
|
97
|
+
"Parent - Single",
|
|
98
|
+
() => relational.Assign(),
|
|
99
|
+
() => manual.Assign()
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
DestroyImmediate(child);
|
|
103
|
+
DestroyImmediate(root);
|
|
104
|
+
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private static ScenarioResult RunParentArrayScenario()
|
|
109
|
+
{
|
|
110
|
+
GameObject grandParent = CreateGameObject("ParentArrayGrand");
|
|
111
|
+
grandParent.AddComponent<BoxCollider>();
|
|
112
|
+
|
|
113
|
+
GameObject parent = CreateGameObject("ParentArrayParent");
|
|
114
|
+
parent.transform.SetParent(grandParent.transform, false);
|
|
115
|
+
parent.AddComponent<BoxCollider>();
|
|
116
|
+
|
|
117
|
+
GameObject child = CreateGameObject("ParentArrayChild");
|
|
118
|
+
child.transform.SetParent(parent.transform, false);
|
|
119
|
+
|
|
120
|
+
ParentArrayRelational relational = child.AddComponent<ParentArrayRelational>();
|
|
121
|
+
ParentArrayManual manual = child.AddComponent<ParentArrayManual>();
|
|
122
|
+
|
|
123
|
+
ScenarioResult result = ExecuteScenario(
|
|
124
|
+
"Parent - Array",
|
|
125
|
+
() => relational.Assign(),
|
|
126
|
+
() => manual.Assign()
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
DestroyImmediate(child);
|
|
130
|
+
DestroyImmediate(parent);
|
|
131
|
+
DestroyImmediate(grandParent);
|
|
132
|
+
|
|
133
|
+
return result;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private static ScenarioResult RunParentListScenario()
|
|
137
|
+
{
|
|
138
|
+
GameObject grandParent = CreateGameObject("ParentListGrand");
|
|
139
|
+
grandParent.AddComponent<BoxCollider>();
|
|
140
|
+
|
|
141
|
+
GameObject parent = CreateGameObject("ParentListParent");
|
|
142
|
+
parent.transform.SetParent(grandParent.transform, false);
|
|
143
|
+
parent.AddComponent<BoxCollider>();
|
|
144
|
+
|
|
145
|
+
GameObject child = CreateGameObject("ParentListChild");
|
|
146
|
+
child.transform.SetParent(parent.transform, false);
|
|
147
|
+
|
|
148
|
+
ParentListRelational relational = child.AddComponent<ParentListRelational>();
|
|
149
|
+
ParentListManual manual = child.AddComponent<ParentListManual>();
|
|
150
|
+
|
|
151
|
+
ScenarioResult result = ExecuteScenario(
|
|
152
|
+
"Parent - List",
|
|
153
|
+
() => relational.Assign(),
|
|
154
|
+
() => manual.Assign()
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
DestroyImmediate(child);
|
|
158
|
+
DestroyImmediate(parent);
|
|
159
|
+
DestroyImmediate(grandParent);
|
|
160
|
+
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private static ScenarioResult RunParentHashSetScenario()
|
|
165
|
+
{
|
|
166
|
+
GameObject grandParent = CreateGameObject("ParentHashGrand");
|
|
167
|
+
grandParent.AddComponent<BoxCollider>();
|
|
168
|
+
|
|
169
|
+
GameObject parent = CreateGameObject("ParentHashParent");
|
|
170
|
+
parent.transform.SetParent(grandParent.transform, false);
|
|
171
|
+
parent.AddComponent<BoxCollider>();
|
|
172
|
+
|
|
173
|
+
GameObject child = CreateGameObject("ParentHashChild");
|
|
174
|
+
child.transform.SetParent(parent.transform, false);
|
|
175
|
+
|
|
176
|
+
ParentHashSetRelational relational = child.AddComponent<ParentHashSetRelational>();
|
|
177
|
+
ParentHashSetManual manual = child.AddComponent<ParentHashSetManual>();
|
|
178
|
+
|
|
179
|
+
ScenarioResult result = ExecuteScenario(
|
|
180
|
+
"Parent - HashSet",
|
|
181
|
+
() => relational.Assign(),
|
|
182
|
+
() => manual.Assign()
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
DestroyImmediate(child);
|
|
186
|
+
DestroyImmediate(parent);
|
|
187
|
+
DestroyImmediate(grandParent);
|
|
188
|
+
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
private static ScenarioResult RunChildSingleScenario()
|
|
193
|
+
{
|
|
194
|
+
GameObject parent = CreateGameObject("ChildSingleParent");
|
|
195
|
+
|
|
196
|
+
ChildSingleRelational relational = parent.AddComponent<ChildSingleRelational>();
|
|
197
|
+
ChildSingleManual manual = parent.AddComponent<ChildSingleManual>();
|
|
198
|
+
|
|
199
|
+
GameObject child = CreateGameObject("ChildSingleChild");
|
|
200
|
+
child.AddComponent<BoxCollider>();
|
|
201
|
+
child.transform.SetParent(parent.transform, false);
|
|
202
|
+
|
|
203
|
+
ScenarioResult result = ExecuteScenario(
|
|
204
|
+
"Child - Single",
|
|
205
|
+
() => relational.Assign(),
|
|
206
|
+
() => manual.Assign()
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
DestroyImmediate(child);
|
|
210
|
+
DestroyImmediate(parent);
|
|
211
|
+
|
|
212
|
+
return result;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
private static ScenarioResult RunChildArrayScenario()
|
|
216
|
+
{
|
|
217
|
+
GameObject parent = CreateGameObject("ChildArrayParent");
|
|
218
|
+
|
|
219
|
+
ChildArrayRelational relational = parent.AddComponent<ChildArrayRelational>();
|
|
220
|
+
ChildArrayManual manual = parent.AddComponent<ChildArrayManual>();
|
|
221
|
+
|
|
222
|
+
List<GameObject> children = CreateChildColliders(parent, 6);
|
|
223
|
+
|
|
224
|
+
ScenarioResult result = ExecuteScenario(
|
|
225
|
+
"Child - Array",
|
|
226
|
+
() => relational.Assign(),
|
|
227
|
+
() => manual.Assign()
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
DestroyChildren(children);
|
|
231
|
+
DestroyImmediate(parent);
|
|
232
|
+
|
|
233
|
+
return result;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
private static ScenarioResult RunChildListScenario()
|
|
237
|
+
{
|
|
238
|
+
GameObject parent = CreateGameObject("ChildListParent");
|
|
239
|
+
|
|
240
|
+
ChildListRelational relational = parent.AddComponent<ChildListRelational>();
|
|
241
|
+
ChildListManual manual = parent.AddComponent<ChildListManual>();
|
|
242
|
+
|
|
243
|
+
List<GameObject> children = CreateChildColliders(parent, 8);
|
|
244
|
+
|
|
245
|
+
ScenarioResult result = ExecuteScenario(
|
|
246
|
+
"Child - List",
|
|
247
|
+
() => relational.Assign(),
|
|
248
|
+
() => manual.Assign()
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
DestroyChildren(children);
|
|
252
|
+
DestroyImmediate(parent);
|
|
253
|
+
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
private static ScenarioResult RunChildHashSetScenario()
|
|
258
|
+
{
|
|
259
|
+
GameObject parent = CreateGameObject("ChildHashParent");
|
|
260
|
+
|
|
261
|
+
ChildHashSetRelational relational = parent.AddComponent<ChildHashSetRelational>();
|
|
262
|
+
ChildHashSetManual manual = parent.AddComponent<ChildHashSetManual>();
|
|
263
|
+
|
|
264
|
+
List<GameObject> children = CreateChildColliders(parent, 8);
|
|
265
|
+
|
|
266
|
+
ScenarioResult result = ExecuteScenario(
|
|
267
|
+
"Child - HashSet",
|
|
268
|
+
() => relational.Assign(),
|
|
269
|
+
() => manual.Assign()
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
DestroyChildren(children);
|
|
273
|
+
DestroyImmediate(parent);
|
|
274
|
+
|
|
275
|
+
return result;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private static ScenarioResult RunSiblingSingleScenario()
|
|
279
|
+
{
|
|
280
|
+
GameObject host = CreateGameObject("SiblingSingleHost");
|
|
281
|
+
|
|
282
|
+
SiblingSingleRelational relational = host.AddComponent<SiblingSingleRelational>();
|
|
283
|
+
SiblingSingleManual manual = host.AddComponent<SiblingSingleManual>();
|
|
284
|
+
|
|
285
|
+
host.AddComponent<BoxCollider>();
|
|
286
|
+
|
|
287
|
+
ScenarioResult result = ExecuteScenario(
|
|
288
|
+
"Sibling - Single",
|
|
289
|
+
() => relational.Assign(),
|
|
290
|
+
() => manual.Assign()
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
DestroyImmediate(host);
|
|
294
|
+
|
|
295
|
+
return result;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private static ScenarioResult RunSiblingArrayScenario()
|
|
299
|
+
{
|
|
300
|
+
GameObject host = CreateGameObject("SiblingArrayHost");
|
|
301
|
+
|
|
302
|
+
SiblingArrayRelational relational = host.AddComponent<SiblingArrayRelational>();
|
|
303
|
+
SiblingArrayManual manual = host.AddComponent<SiblingArrayManual>();
|
|
304
|
+
|
|
305
|
+
AddSiblingColliders(host, 6);
|
|
306
|
+
|
|
307
|
+
ScenarioResult result = ExecuteScenario(
|
|
308
|
+
"Sibling - Array",
|
|
309
|
+
() => relational.Assign(),
|
|
310
|
+
() => manual.Assign()
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
DestroyImmediate(host);
|
|
314
|
+
|
|
315
|
+
return result;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private static ScenarioResult RunSiblingListScenario()
|
|
319
|
+
{
|
|
320
|
+
GameObject host = CreateGameObject("SiblingListHost");
|
|
321
|
+
|
|
322
|
+
SiblingListRelational relational = host.AddComponent<SiblingListRelational>();
|
|
323
|
+
SiblingListManual manual = host.AddComponent<SiblingListManual>();
|
|
324
|
+
|
|
325
|
+
AddSiblingColliders(host, 8);
|
|
326
|
+
|
|
327
|
+
ScenarioResult result = ExecuteScenario(
|
|
328
|
+
"Sibling - List",
|
|
329
|
+
() => relational.Assign(),
|
|
330
|
+
() => manual.Assign()
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
DestroyImmediate(host);
|
|
334
|
+
|
|
335
|
+
return result;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
private static ScenarioResult RunSiblingHashSetScenario()
|
|
339
|
+
{
|
|
340
|
+
GameObject host = CreateGameObject("SiblingHashHost");
|
|
341
|
+
|
|
342
|
+
SiblingHashSetRelational relational = host.AddComponent<SiblingHashSetRelational>();
|
|
343
|
+
SiblingHashSetManual manual = host.AddComponent<SiblingHashSetManual>();
|
|
344
|
+
|
|
345
|
+
AddSiblingColliders(host, 8);
|
|
346
|
+
|
|
347
|
+
ScenarioResult result = ExecuteScenario(
|
|
348
|
+
"Sibling - HashSet",
|
|
349
|
+
() => relational.Assign(),
|
|
350
|
+
() => manual.Assign()
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
DestroyImmediate(host);
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private static ScenarioResult ExecuteScenario(
|
|
358
|
+
string label,
|
|
359
|
+
Action relationalAction,
|
|
360
|
+
Action manualAction
|
|
361
|
+
)
|
|
362
|
+
{
|
|
363
|
+
if (relationalAction == null)
|
|
364
|
+
{
|
|
365
|
+
throw new ArgumentNullException(nameof(relationalAction));
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
if (manualAction == null)
|
|
369
|
+
{
|
|
370
|
+
throw new ArgumentNullException(nameof(manualAction));
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
Prewarm(relationalAction);
|
|
374
|
+
Prewarm(manualAction);
|
|
375
|
+
|
|
376
|
+
BenchmarkMetrics relationalMetrics = Measure(relationalAction);
|
|
377
|
+
BenchmarkMetrics manualMetrics = Measure(manualAction);
|
|
378
|
+
|
|
379
|
+
return new ScenarioResult(label, relationalMetrics, manualMetrics);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
private static void Prewarm(Action action)
|
|
383
|
+
{
|
|
384
|
+
for (int i = 0; i < 10; ++i)
|
|
385
|
+
{
|
|
386
|
+
action();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
private static BenchmarkMetrics Measure(Action action)
|
|
391
|
+
{
|
|
392
|
+
GC.Collect();
|
|
393
|
+
GC.WaitForPendingFinalizers();
|
|
394
|
+
GC.Collect();
|
|
395
|
+
|
|
396
|
+
Stopwatch stopwatch = Stopwatch.StartNew();
|
|
397
|
+
|
|
398
|
+
int iterations = 0;
|
|
399
|
+
do
|
|
400
|
+
{
|
|
401
|
+
for (int i = 0; i < NumIterations; ++i)
|
|
402
|
+
{
|
|
403
|
+
action();
|
|
404
|
+
++iterations;
|
|
405
|
+
}
|
|
406
|
+
} while (stopwatch.Elapsed < BenchmarkDuration);
|
|
407
|
+
|
|
408
|
+
stopwatch.Stop();
|
|
409
|
+
double opsPerSecond = iterations / stopwatch.Elapsed.TotalSeconds;
|
|
410
|
+
return new BenchmarkMetrics(opsPerSecond, iterations, stopwatch.Elapsed);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private static string FormatOpsRow(ScenarioResult result)
|
|
414
|
+
{
|
|
415
|
+
string ratio =
|
|
416
|
+
result.Manual.OpsPerSecond > 0d
|
|
417
|
+
? (result.Relational.OpsPerSecond / result.Manual.OpsPerSecond).ToString(
|
|
418
|
+
"0.00",
|
|
419
|
+
CultureInfo.InvariantCulture
|
|
420
|
+
) + "x"
|
|
421
|
+
: "n/a";
|
|
422
|
+
|
|
423
|
+
return "| "
|
|
424
|
+
+ result.Label
|
|
425
|
+
+ " | "
|
|
426
|
+
+ FormatOps(result.Relational.OpsPerSecond)
|
|
427
|
+
+ " | "
|
|
428
|
+
+ FormatOps(result.Manual.OpsPerSecond)
|
|
429
|
+
+ " | "
|
|
430
|
+
+ ratio
|
|
431
|
+
+ " | "
|
|
432
|
+
+ result.Relational.Iterations.ToString("N0", CultureInfo.InvariantCulture)
|
|
433
|
+
+ " |";
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private static string FormatOps(double value)
|
|
437
|
+
{
|
|
438
|
+
if (value >= 1000d)
|
|
439
|
+
{
|
|
440
|
+
return value.ToString("N0", CultureInfo.InvariantCulture);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (value >= 100d)
|
|
444
|
+
{
|
|
445
|
+
return value.ToString("N1", CultureInfo.InvariantCulture);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
return value.ToString("0.00", CultureInfo.InvariantCulture);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private static GameObject CreateGameObject(string name)
|
|
452
|
+
{
|
|
453
|
+
return new GameObject(name);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
private static List<GameObject> CreateChildColliders(GameObject parent, int count)
|
|
457
|
+
{
|
|
458
|
+
List<GameObject> children = new(count);
|
|
459
|
+
for (int i = 0; i < count; ++i)
|
|
460
|
+
{
|
|
461
|
+
GameObject child = CreateGameObject($"Child_{i}");
|
|
462
|
+
child.AddComponent<BoxCollider>();
|
|
463
|
+
child.transform.SetParent(parent.transform, false);
|
|
464
|
+
children.Add(child);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
return children;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
private static void AddSiblingColliders(GameObject host, int count)
|
|
471
|
+
{
|
|
472
|
+
for (int i = 0; i < count; ++i)
|
|
473
|
+
{
|
|
474
|
+
host.AddComponent<BoxCollider>();
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
private static void DestroyChildren(IEnumerable<GameObject> children)
|
|
479
|
+
{
|
|
480
|
+
if (children == null)
|
|
481
|
+
{
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
foreach (GameObject child in children)
|
|
486
|
+
{
|
|
487
|
+
DestroyImmediate(child);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private static void DestroyImmediate(UnityEngine.Object obj)
|
|
492
|
+
{
|
|
493
|
+
if (obj != null)
|
|
494
|
+
{
|
|
495
|
+
UnityEngine.Object.DestroyImmediate(obj);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private static string GetOperatingSystemToken()
|
|
500
|
+
{
|
|
501
|
+
RuntimePlatform platform = Application.platform;
|
|
502
|
+
switch (platform)
|
|
503
|
+
{
|
|
504
|
+
case RuntimePlatform.WindowsEditor:
|
|
505
|
+
case RuntimePlatform.WindowsPlayer:
|
|
506
|
+
case RuntimePlatform.WindowsServer:
|
|
507
|
+
return "WINDOWS";
|
|
508
|
+
case RuntimePlatform.OSXEditor:
|
|
509
|
+
case RuntimePlatform.OSXPlayer:
|
|
510
|
+
return "MACOS";
|
|
511
|
+
case RuntimePlatform.LinuxEditor:
|
|
512
|
+
case RuntimePlatform.LinuxPlayer:
|
|
513
|
+
case RuntimePlatform.LinuxServer:
|
|
514
|
+
return "LINUX";
|
|
515
|
+
default:
|
|
516
|
+
return "OTHER";
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private readonly struct ScenarioResult
|
|
521
|
+
{
|
|
522
|
+
public ScenarioResult(
|
|
523
|
+
string label,
|
|
524
|
+
BenchmarkMetrics relational,
|
|
525
|
+
BenchmarkMetrics manual
|
|
526
|
+
)
|
|
527
|
+
{
|
|
528
|
+
Label = label;
|
|
529
|
+
Relational = relational;
|
|
530
|
+
Manual = manual;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
public string Label { get; }
|
|
534
|
+
|
|
535
|
+
public BenchmarkMetrics Relational { get; }
|
|
536
|
+
|
|
537
|
+
public BenchmarkMetrics Manual { get; }
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
private readonly struct BenchmarkMetrics
|
|
541
|
+
{
|
|
542
|
+
public BenchmarkMetrics(double opsPerSecond, int iterations, TimeSpan elapsed)
|
|
543
|
+
{
|
|
544
|
+
OpsPerSecond = opsPerSecond;
|
|
545
|
+
Iterations = iterations;
|
|
546
|
+
Elapsed = elapsed;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
public double OpsPerSecond { get; }
|
|
550
|
+
|
|
551
|
+
public int Iterations { get; }
|
|
552
|
+
|
|
553
|
+
public TimeSpan Elapsed { get; }
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
private sealed class ParentSingleRelational : MonoBehaviour
|
|
557
|
+
{
|
|
558
|
+
[ParentComponent]
|
|
559
|
+
private BoxCollider parentCollider;
|
|
560
|
+
|
|
561
|
+
public void Assign()
|
|
562
|
+
{
|
|
563
|
+
this.AssignParentComponents();
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
private sealed class ParentSingleManual : MonoBehaviour
|
|
568
|
+
{
|
|
569
|
+
private BoxCollider parentCollider;
|
|
570
|
+
|
|
571
|
+
public void Assign()
|
|
572
|
+
{
|
|
573
|
+
parentCollider = GetComponentInParent<BoxCollider>();
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
private sealed class ParentArrayRelational : MonoBehaviour
|
|
578
|
+
{
|
|
579
|
+
[ParentComponent]
|
|
580
|
+
private BoxCollider[] parentColliders;
|
|
581
|
+
|
|
582
|
+
public void Assign()
|
|
583
|
+
{
|
|
584
|
+
this.AssignParentComponents();
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
private sealed class ParentArrayManual : MonoBehaviour
|
|
589
|
+
{
|
|
590
|
+
private BoxCollider[] parentColliders;
|
|
591
|
+
|
|
592
|
+
public void Assign()
|
|
593
|
+
{
|
|
594
|
+
parentColliders = GetComponentsInParent<BoxCollider>();
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
private sealed class ParentListRelational : MonoBehaviour
|
|
599
|
+
{
|
|
600
|
+
[ParentComponent]
|
|
601
|
+
private List<BoxCollider> parentColliders = new();
|
|
602
|
+
|
|
603
|
+
public void Assign()
|
|
604
|
+
{
|
|
605
|
+
this.AssignParentComponents();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
private sealed class ParentListManual : MonoBehaviour
|
|
610
|
+
{
|
|
611
|
+
private readonly List<BoxCollider> parentColliders = new();
|
|
612
|
+
|
|
613
|
+
public void Assign()
|
|
614
|
+
{
|
|
615
|
+
GetComponentsInParent(false, parentColliders);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
private sealed class ParentHashSetRelational : MonoBehaviour
|
|
620
|
+
{
|
|
621
|
+
[ParentComponent]
|
|
622
|
+
private HashSet<BoxCollider> parentColliders = new();
|
|
623
|
+
|
|
624
|
+
public void Assign()
|
|
625
|
+
{
|
|
626
|
+
this.AssignParentComponents();
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
private sealed class ParentHashSetManual : MonoBehaviour
|
|
631
|
+
{
|
|
632
|
+
private readonly HashSet<BoxCollider> parentColliders = new();
|
|
633
|
+
|
|
634
|
+
public void Assign()
|
|
635
|
+
{
|
|
636
|
+
BoxCollider[] buffer = GetComponentsInParent<BoxCollider>();
|
|
637
|
+
parentColliders.Clear();
|
|
638
|
+
for (int i = 0; i < buffer.Length; ++i)
|
|
639
|
+
{
|
|
640
|
+
parentColliders.Add(buffer[i]);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
private sealed class ChildSingleRelational : MonoBehaviour
|
|
646
|
+
{
|
|
647
|
+
[ChildComponent(OnlyDescendants = true)]
|
|
648
|
+
private BoxCollider childCollider;
|
|
649
|
+
|
|
650
|
+
public void Assign()
|
|
651
|
+
{
|
|
652
|
+
this.AssignChildComponents();
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
private sealed class ChildSingleManual : MonoBehaviour
|
|
657
|
+
{
|
|
658
|
+
private BoxCollider childCollider;
|
|
659
|
+
|
|
660
|
+
public void Assign()
|
|
661
|
+
{
|
|
662
|
+
BoxCollider[] buffer = GetComponentsInChildren<BoxCollider>();
|
|
663
|
+
childCollider = buffer.Length > 0 ? buffer[0] : null;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
private sealed class ChildArrayRelational : MonoBehaviour
|
|
668
|
+
{
|
|
669
|
+
[ChildComponent(OnlyDescendants = true)]
|
|
670
|
+
private BoxCollider[] childColliders;
|
|
671
|
+
|
|
672
|
+
public void Assign()
|
|
673
|
+
{
|
|
674
|
+
this.AssignChildComponents();
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
private sealed class ChildArrayManual : MonoBehaviour
|
|
679
|
+
{
|
|
680
|
+
private BoxCollider[] childColliders;
|
|
681
|
+
|
|
682
|
+
public void Assign()
|
|
683
|
+
{
|
|
684
|
+
BoxCollider[] buffer = GetComponentsInChildren<BoxCollider>();
|
|
685
|
+
if (buffer.Length == 0)
|
|
686
|
+
{
|
|
687
|
+
childColliders = Array.Empty<BoxCollider>();
|
|
688
|
+
}
|
|
689
|
+
else
|
|
690
|
+
{
|
|
691
|
+
childColliders = buffer;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
private sealed class ChildListRelational : MonoBehaviour
|
|
697
|
+
{
|
|
698
|
+
[ChildComponent(OnlyDescendants = true)]
|
|
699
|
+
private List<BoxCollider> childColliders = new();
|
|
700
|
+
|
|
701
|
+
public void Assign()
|
|
702
|
+
{
|
|
703
|
+
this.AssignChildComponents();
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
private sealed class ChildListManual : MonoBehaviour
|
|
708
|
+
{
|
|
709
|
+
private readonly List<BoxCollider> childColliders = new();
|
|
710
|
+
|
|
711
|
+
public void Assign()
|
|
712
|
+
{
|
|
713
|
+
GetComponentsInChildren(childColliders);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
private sealed class ChildHashSetRelational : MonoBehaviour
|
|
718
|
+
{
|
|
719
|
+
[ChildComponent(OnlyDescendants = true)]
|
|
720
|
+
private HashSet<BoxCollider> childColliders = new();
|
|
721
|
+
|
|
722
|
+
public void Assign()
|
|
723
|
+
{
|
|
724
|
+
this.AssignChildComponents();
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
private sealed class ChildHashSetManual : MonoBehaviour
|
|
729
|
+
{
|
|
730
|
+
private readonly HashSet<BoxCollider> childColliders = new();
|
|
731
|
+
|
|
732
|
+
public void Assign()
|
|
733
|
+
{
|
|
734
|
+
BoxCollider[] buffer = GetComponentsInChildren<BoxCollider>();
|
|
735
|
+
childColliders.Clear();
|
|
736
|
+
for (int i = 0; i < buffer.Length; ++i)
|
|
737
|
+
{
|
|
738
|
+
childColliders.Add(buffer[i]);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
private sealed class SiblingSingleRelational : MonoBehaviour
|
|
744
|
+
{
|
|
745
|
+
[SiblingComponent]
|
|
746
|
+
private BoxCollider siblingCollider;
|
|
747
|
+
|
|
748
|
+
public void Assign()
|
|
749
|
+
{
|
|
750
|
+
this.AssignSiblingComponents();
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
private sealed class SiblingSingleManual : MonoBehaviour
|
|
755
|
+
{
|
|
756
|
+
private BoxCollider siblingCollider;
|
|
757
|
+
|
|
758
|
+
public void Assign()
|
|
759
|
+
{
|
|
760
|
+
siblingCollider = GetComponent<BoxCollider>();
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
private sealed class SiblingArrayRelational : MonoBehaviour
|
|
765
|
+
{
|
|
766
|
+
[SiblingComponent]
|
|
767
|
+
private BoxCollider[] siblingColliders;
|
|
768
|
+
|
|
769
|
+
public void Assign()
|
|
770
|
+
{
|
|
771
|
+
this.AssignSiblingComponents();
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
private sealed class SiblingArrayManual : MonoBehaviour
|
|
776
|
+
{
|
|
777
|
+
private BoxCollider[] siblingColliders;
|
|
778
|
+
|
|
779
|
+
public void Assign()
|
|
780
|
+
{
|
|
781
|
+
siblingColliders = GetComponents<BoxCollider>();
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
private sealed class SiblingListRelational : MonoBehaviour
|
|
786
|
+
{
|
|
787
|
+
[SiblingComponent]
|
|
788
|
+
private List<BoxCollider> siblingColliders = new();
|
|
789
|
+
|
|
790
|
+
public void Assign()
|
|
791
|
+
{
|
|
792
|
+
this.AssignSiblingComponents();
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
private sealed class SiblingListManual : MonoBehaviour
|
|
797
|
+
{
|
|
798
|
+
private readonly List<BoxCollider> siblingColliders = new();
|
|
799
|
+
|
|
800
|
+
public void Assign()
|
|
801
|
+
{
|
|
802
|
+
GetComponents(siblingColliders);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
private sealed class SiblingHashSetRelational : MonoBehaviour
|
|
807
|
+
{
|
|
808
|
+
[SiblingComponent]
|
|
809
|
+
private HashSet<BoxCollider> siblingColliders = new();
|
|
810
|
+
|
|
811
|
+
public void Assign()
|
|
812
|
+
{
|
|
813
|
+
this.AssignSiblingComponents();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
private sealed class SiblingHashSetManual : MonoBehaviour
|
|
818
|
+
{
|
|
819
|
+
private readonly HashSet<BoxCollider> siblingColliders = new();
|
|
820
|
+
|
|
821
|
+
public void Assign()
|
|
822
|
+
{
|
|
823
|
+
BoxCollider[] buffer = GetComponents<BoxCollider>();
|
|
824
|
+
siblingColliders.Clear();
|
|
825
|
+
for (int i = 0; i < buffer.Length; ++i)
|
|
826
|
+
{
|
|
827
|
+
siblingColliders.Add(buffer[i]);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|