com.wallstop-studios.unity-helpers 2.1.0 → 2.1.2
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 +92 -0
- package/{package-lock.json.meta → Docs/ILIST_SORTING_PERFORMANCE.md.meta} +1 -1
- package/Docs/INDEX.md +11 -1
- package/Docs/Images/random_generators.svg +7 -7
- package/Docs/RANDOM_PERFORMANCE.md +17 -14
- package/Docs/REFLECTION_HELPERS.md +84 -1
- package/Docs/REFLECTION_PERFORMANCE.md +169 -0
- package/Docs/REFLECTION_PERFORMANCE.md.meta +7 -0
- 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/Core/Helper/AnimationEventHelpers.cs +1 -1
- 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 +33 -18
- 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/Extension/IListExtensions.cs +720 -12
- 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/Helper/UnityMainThreadDispatcher.cs +2 -3
- package/Runtime/Core/Math/Line2D.cs +2 -4
- package/Runtime/Core/Math/Line3D.cs +2 -4
- package/Runtime/Core/Random/AbstractRandom.cs +52 -5
- package/Runtime/Core/Random/DotNetRandom.cs +3 -3
- package/Runtime/Core/Random/FlurryBurstRandom.cs +279 -0
- package/Runtime/Core/Random/FlurryBurstRandom.cs.meta +3 -0
- package/Runtime/Core/Random/IllusionFlow.cs +3 -3
- package/Runtime/Core/Random/LinearCongruentialGenerator.cs +3 -3
- package/Runtime/Core/Random/PcgRandom.cs +6 -6
- package/Runtime/Core/Random/PhotonSpinRandom.cs +387 -0
- package/Runtime/Core/Random/PhotonSpinRandom.cs.meta +3 -0
- package/Runtime/Core/Random/RomuDuo.cs +3 -3
- package/Runtime/Core/Random/SplitMix64.cs +3 -3
- package/Runtime/Core/Random/SquirrelRandom.cs +6 -4
- package/Runtime/Core/Random/StormDropRandom.cs +271 -0
- package/Runtime/Core/Random/StormDropRandom.cs.meta +3 -0
- package/Runtime/Core/Random/UnityRandom.cs +3 -3
- package/Runtime/Core/Random/WyRandom.cs +6 -4
- package/Runtime/Core/Random/XorShiftRandom.cs +3 -3
- package/Runtime/Core/Random/XoroShiroRandom.cs +3 -3
- package/Runtime/Tags/AttributeMetadataCache.cs +316 -9
- 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/Editor/Tags/AttributeMetadataCacheTests.cs +192 -0
- package/Tests/Editor/Tags/AttributeMetadataCacheTests.cs.meta +11 -0
- package/Tests/Editor/Tags.meta +8 -0
- package/Tests/Runtime/Extensions/IListExtensionTests.cs +187 -1
- package/Tests/Runtime/Helper/ObjectsTests.cs +4 -4
- 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/Integrations/Reflex/RelationalComponentsReflexTests.cs +2 -2
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs +346 -0
- package/Tests/Runtime/Performance/IListSortingPerformanceTests.cs.meta +11 -0
- package/Tests/Runtime/Performance/RandomPerformanceTests.cs +3 -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/Tests/Runtime/Random/FlurryBurstRandomTests.cs +12 -0
- package/Tests/Runtime/Random/FlurryBurstRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs +12 -0
- package/Tests/Runtime/Random/PhotonSpinRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Random/RandomProtoSerializationTests.cs +14 -0
- package/Tests/Runtime/Random/RandomTestBase.cs +39 -4
- package/Tests/Runtime/Random/StormDropRandomTests.cs +12 -0
- package/Tests/Runtime/Random/StormDropRandomTests.cs.meta +3 -0
- package/Tests/Runtime/Serialization/ProtoInterfaceResolutionEdgeTests.cs +2 -2
- package/Tests/Runtime/Serialization/ProtoRootRegistrationTests.cs +1 -1
- package/Tests/Runtime/Serialization/ProtoSerializeBehaviorTests.cs +1 -1
- package/Tests/Runtime/Tags/PeriodicEffectDefinitionSerializationTests.cs +2 -2
- 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,1238 @@
|
|
|
1
|
+
namespace WallstopStudios.UnityHelpers.Tests.Performance
|
|
2
|
+
{
|
|
3
|
+
using System;
|
|
4
|
+
using System.Collections.Generic;
|
|
5
|
+
using System.Diagnostics;
|
|
6
|
+
using System.Reflection;
|
|
7
|
+
using System.Runtime.InteropServices;
|
|
8
|
+
using NUnit.Framework;
|
|
9
|
+
using WallstopStudios.UnityHelpers.Core.Helper;
|
|
10
|
+
|
|
11
|
+
public sealed class ReflectionPerformanceTests
|
|
12
|
+
{
|
|
13
|
+
private const int BatchSize = 256;
|
|
14
|
+
private static readonly TimeSpan BenchmarkDuration = TimeSpan.FromMilliseconds(250);
|
|
15
|
+
private static int sink;
|
|
16
|
+
|
|
17
|
+
[Test]
|
|
18
|
+
[Timeout(0)]
|
|
19
|
+
public void Benchmark()
|
|
20
|
+
{
|
|
21
|
+
StrategyConfig[] strategies =
|
|
22
|
+
{
|
|
23
|
+
new StrategyConfig("Default (auto)", null, null),
|
|
24
|
+
new StrategyConfig("Expressions", true, false),
|
|
25
|
+
new StrategyConfig("Dynamic IL", false, true),
|
|
26
|
+
new StrategyConfig("Reflection Fallback", false, false),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
List<StrategyRunResult> supportedRuns = new List<StrategyRunResult>();
|
|
30
|
+
|
|
31
|
+
foreach (StrategyConfig config in strategies)
|
|
32
|
+
{
|
|
33
|
+
StrategyRunResult result = RunStrategy(config);
|
|
34
|
+
if (!result.Supported)
|
|
35
|
+
{
|
|
36
|
+
UnityEngine.Debug.LogWarning(
|
|
37
|
+
$"[ReflectionPerf] Skipping {config.Label}: {result.SkipReason}"
|
|
38
|
+
);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
supportedRuns.Add(result);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
List<string> outputLines = new List<string>
|
|
46
|
+
{
|
|
47
|
+
string.Format(
|
|
48
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
49
|
+
"Generated on {0:yyyy-MM-dd HH:mm:ss} UTC",
|
|
50
|
+
DateTime.UtcNow
|
|
51
|
+
),
|
|
52
|
+
string.Empty,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
foreach (StrategyRunResult run in supportedRuns)
|
|
56
|
+
{
|
|
57
|
+
outputLines.Add($"### Strategy: {run.Label}");
|
|
58
|
+
outputLines.Add(string.Empty);
|
|
59
|
+
outputLines.Add("#### Boxed Access (object)");
|
|
60
|
+
outputLines.Add(string.Empty);
|
|
61
|
+
outputLines.Add(
|
|
62
|
+
"| Scenario | Helper (ops/sec) | System.Reflection (ops/sec) | Speedup vs Reflection |"
|
|
63
|
+
);
|
|
64
|
+
outputLines.Add(
|
|
65
|
+
"| -------- | ---------------- | --------------------------- | --------------------- |"
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
foreach (ScenarioResult result in run.BoxedResults)
|
|
69
|
+
{
|
|
70
|
+
string row = string.Format(
|
|
71
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
72
|
+
"| {0} | {1} | {2} | {3:F2}x |",
|
|
73
|
+
result.Name,
|
|
74
|
+
FormatOps(result.HelperOpsPerSecond),
|
|
75
|
+
FormatOps(result.BaselineOpsPerSecond),
|
|
76
|
+
result.Speedup
|
|
77
|
+
);
|
|
78
|
+
outputLines.Add(row);
|
|
79
|
+
UnityEngine.Debug.Log(
|
|
80
|
+
string.Format(
|
|
81
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
82
|
+
"[ReflectionPerf][{0}][Boxed] {1}: helpers={2:N0} ops/s, reflection={3:N0} ops/s",
|
|
83
|
+
run.Label,
|
|
84
|
+
result.Name,
|
|
85
|
+
result.HelperOpsPerSecond,
|
|
86
|
+
result.BaselineOpsPerSecond
|
|
87
|
+
)
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
outputLines.Add(string.Empty);
|
|
92
|
+
outputLines.Add("#### Typed Access (no boxing)");
|
|
93
|
+
outputLines.Add(string.Empty);
|
|
94
|
+
outputLines.Add(
|
|
95
|
+
"| Scenario | Helper (ops/sec) | Baseline Delegate (ops/sec) | System.Reflection (ops/sec) | Speedup vs Delegate | Speedup vs Reflection |"
|
|
96
|
+
);
|
|
97
|
+
outputLines.Add(
|
|
98
|
+
"| -------- | ---------------- | --------------------------- | --------------------------- | ------------------- | -------------------- |"
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
Dictionary<string, double> reflectionBaselineLookup = new Dictionary<
|
|
102
|
+
string,
|
|
103
|
+
double
|
|
104
|
+
>(StringComparer.Ordinal);
|
|
105
|
+
foreach (ScenarioResult boxed in run.BoxedResults)
|
|
106
|
+
{
|
|
107
|
+
reflectionBaselineLookup[GetScenarioKey(boxed.Name)] =
|
|
108
|
+
boxed.BaselineOpsPerSecond;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
foreach (ScenarioResult result in run.TypedResults)
|
|
112
|
+
{
|
|
113
|
+
double reflectionOps = reflectionBaselineLookup.TryGetValue(
|
|
114
|
+
GetScenarioKey(result.Name),
|
|
115
|
+
out double value
|
|
116
|
+
)
|
|
117
|
+
? value
|
|
118
|
+
: double.NaN;
|
|
119
|
+
double speedupVsReflection =
|
|
120
|
+
reflectionOps <= 0.0
|
|
121
|
+
? double.PositiveInfinity
|
|
122
|
+
: result.HelperOpsPerSecond / reflectionOps;
|
|
123
|
+
|
|
124
|
+
string row = string.Format(
|
|
125
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
126
|
+
"| {0} | {1} | {2} | {3} | {4:F2}x | {5:F2}x |",
|
|
127
|
+
result.Name,
|
|
128
|
+
FormatOps(result.HelperOpsPerSecond),
|
|
129
|
+
FormatOps(result.BaselineOpsPerSecond),
|
|
130
|
+
FormatOps(reflectionOps),
|
|
131
|
+
result.Speedup,
|
|
132
|
+
speedupVsReflection
|
|
133
|
+
);
|
|
134
|
+
outputLines.Add(row);
|
|
135
|
+
UnityEngine.Debug.Log(
|
|
136
|
+
string.Format(
|
|
137
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
138
|
+
"[ReflectionPerf][{0}][Typed] {1}: helpers={2:N0} ops/s, delegate={3:N0} ops/s, reflection={4:N0} ops/s",
|
|
139
|
+
run.Label,
|
|
140
|
+
result.Name,
|
|
141
|
+
result.HelperOpsPerSecond,
|
|
142
|
+
result.BaselineOpsPerSecond,
|
|
143
|
+
reflectionOps
|
|
144
|
+
)
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
outputLines.Add(string.Empty);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
string token = string.Format(
|
|
152
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
153
|
+
"REFLECTION_PERFORMANCE_{0}",
|
|
154
|
+
GetOsToken()
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
BenchmarkReadmeUpdater.UpdateSection(
|
|
158
|
+
token,
|
|
159
|
+
outputLines,
|
|
160
|
+
"Docs/REFLECTION_PERFORMANCE.md"
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private static ReflectionPerfTarget CreateTargetInstance()
|
|
165
|
+
{
|
|
166
|
+
ReflectionPerfTarget instance = new ReflectionPerfTarget
|
|
167
|
+
{
|
|
168
|
+
InstanceField = 5,
|
|
169
|
+
InstanceProperty = 7,
|
|
170
|
+
};
|
|
171
|
+
ReflectionPerfTarget.StaticField = 11;
|
|
172
|
+
ReflectionPerfTarget.StaticProperty = 13;
|
|
173
|
+
return instance;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private static StrategyRunResult RunStrategy(StrategyConfig config)
|
|
177
|
+
{
|
|
178
|
+
IDisposable capabilityOverride = null;
|
|
179
|
+
|
|
180
|
+
try
|
|
181
|
+
{
|
|
182
|
+
if (config.RequiresOverride)
|
|
183
|
+
{
|
|
184
|
+
capabilityOverride = ReflectionHelpers.OverrideReflectionCapabilities(
|
|
185
|
+
config.ExpressionsOverride,
|
|
186
|
+
config.DynamicIlOverride
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (config.ExpressionsOverride == true && !ReflectionHelpers.ExpressionsEnabled)
|
|
191
|
+
{
|
|
192
|
+
return StrategyRunResult.CreateUnsupported(
|
|
193
|
+
config.Label,
|
|
194
|
+
"Expression compilation is not supported on this runtime."
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (config.DynamicIlOverride == true && !ReflectionHelpers.DynamicIlEnabled)
|
|
199
|
+
{
|
|
200
|
+
return StrategyRunResult.CreateUnsupported(
|
|
201
|
+
config.Label,
|
|
202
|
+
"Dynamic IL emission is not supported on this runtime."
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
List<ScenarioResult> boxed = RunScenarios(
|
|
207
|
+
CreateBoxedScenarios(CreateTargetInstance())
|
|
208
|
+
);
|
|
209
|
+
List<ScenarioResult> typed = RunScenarios(
|
|
210
|
+
CreateTypedScenarios(CreateTargetInstance())
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
return StrategyRunResult.Create(config.Label, boxed, typed);
|
|
214
|
+
}
|
|
215
|
+
finally
|
|
216
|
+
{
|
|
217
|
+
capabilityOverride?.Dispose();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private static List<ScenarioResult> RunScenarios(IEnumerable<Scenario> scenarios)
|
|
222
|
+
{
|
|
223
|
+
List<ScenarioResult> results = new List<ScenarioResult>();
|
|
224
|
+
foreach (Scenario scenario in scenarios)
|
|
225
|
+
{
|
|
226
|
+
results.Add(RunScenario(scenario));
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return results;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
private static IEnumerable<Scenario> CreateBoxedScenarios(ReflectionPerfTarget instance)
|
|
233
|
+
{
|
|
234
|
+
Type targetType = typeof(ReflectionPerfTarget);
|
|
235
|
+
FieldInfo instanceField = targetType.GetField(
|
|
236
|
+
nameof(ReflectionPerfTarget.InstanceField)
|
|
237
|
+
);
|
|
238
|
+
FieldInfo staticField = targetType.GetField(nameof(ReflectionPerfTarget.StaticField));
|
|
239
|
+
PropertyInfo instanceProperty = targetType.GetProperty(
|
|
240
|
+
nameof(ReflectionPerfTarget.InstanceProperty)
|
|
241
|
+
);
|
|
242
|
+
PropertyInfo staticProperty = targetType.GetProperty(
|
|
243
|
+
nameof(ReflectionPerfTarget.StaticProperty)
|
|
244
|
+
);
|
|
245
|
+
MethodInfo instanceMethod = targetType.GetMethod(nameof(ReflectionPerfTarget.Combine));
|
|
246
|
+
MethodInfo staticMethod = targetType.GetMethod(
|
|
247
|
+
nameof(ReflectionPerfTarget.StaticCombine)
|
|
248
|
+
);
|
|
249
|
+
ConstructorInfo constructor = targetType.GetConstructor(new[] { typeof(int) });
|
|
250
|
+
|
|
251
|
+
if (
|
|
252
|
+
instanceField == null
|
|
253
|
+
|| staticField == null
|
|
254
|
+
|| instanceProperty == null
|
|
255
|
+
|| staticProperty == null
|
|
256
|
+
|| instanceMethod == null
|
|
257
|
+
|| staticMethod == null
|
|
258
|
+
|| constructor == null
|
|
259
|
+
)
|
|
260
|
+
{
|
|
261
|
+
throw new InvalidOperationException("ReflectionPerfTarget members not found.");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
Func<object, object> instanceFieldGetter = ReflectionHelpers.GetFieldGetter(
|
|
265
|
+
instanceField
|
|
266
|
+
);
|
|
267
|
+
Action<object, object> instanceFieldSetter = ReflectionHelpers.GetFieldSetter(
|
|
268
|
+
instanceField
|
|
269
|
+
);
|
|
270
|
+
Func<object> staticFieldGetter = ReflectionHelpers.GetStaticFieldGetter(staticField);
|
|
271
|
+
Action<object> staticFieldSetter = ReflectionHelpers.GetStaticFieldSetter(staticField);
|
|
272
|
+
Func<object, object> instancePropertyGetter = ReflectionHelpers.GetPropertyGetter(
|
|
273
|
+
instanceProperty
|
|
274
|
+
);
|
|
275
|
+
Action<object, object> instancePropertySetter = ReflectionHelpers.GetPropertySetter(
|
|
276
|
+
instanceProperty
|
|
277
|
+
);
|
|
278
|
+
Func<object, object> staticPropertyGetter = ReflectionHelpers.GetPropertyGetter(
|
|
279
|
+
staticProperty
|
|
280
|
+
);
|
|
281
|
+
Action<object, object> staticPropertySetter = ReflectionHelpers.GetPropertySetter(
|
|
282
|
+
staticProperty
|
|
283
|
+
);
|
|
284
|
+
Func<object, object[], object> instanceMethodInvoker =
|
|
285
|
+
ReflectionHelpers.GetMethodInvoker(instanceMethod);
|
|
286
|
+
Func<object[], object> staticMethodInvoker = ReflectionHelpers.GetStaticMethodInvoker(
|
|
287
|
+
staticMethod
|
|
288
|
+
);
|
|
289
|
+
Func<object[], object> constructorInvoker = ReflectionHelpers.GetConstructor(
|
|
290
|
+
constructor
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
yield return Scenario.Create(
|
|
294
|
+
"Instance Field Get (boxed)",
|
|
295
|
+
() =>
|
|
296
|
+
{
|
|
297
|
+
int count = 0;
|
|
298
|
+
for (int i = 0; i < BatchSize; i++)
|
|
299
|
+
{
|
|
300
|
+
sink ^= (int)instanceField.GetValue(instance);
|
|
301
|
+
count++;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
return count;
|
|
305
|
+
},
|
|
306
|
+
() =>
|
|
307
|
+
{
|
|
308
|
+
int count = 0;
|
|
309
|
+
for (int i = 0; i < BatchSize; i++)
|
|
310
|
+
{
|
|
311
|
+
sink ^= (int)instanceFieldGetter(instance);
|
|
312
|
+
count++;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return count;
|
|
316
|
+
}
|
|
317
|
+
);
|
|
318
|
+
|
|
319
|
+
yield return Scenario.Create(
|
|
320
|
+
"Instance Field Set (boxed)",
|
|
321
|
+
() =>
|
|
322
|
+
{
|
|
323
|
+
int count = 0;
|
|
324
|
+
int value = 0;
|
|
325
|
+
for (int i = 0; i < BatchSize; i++)
|
|
326
|
+
{
|
|
327
|
+
instanceField.SetValue(instance, value);
|
|
328
|
+
sink ^= instance.InstanceField;
|
|
329
|
+
value++;
|
|
330
|
+
count++;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return count;
|
|
334
|
+
},
|
|
335
|
+
() =>
|
|
336
|
+
{
|
|
337
|
+
int count = 0;
|
|
338
|
+
int value = 0;
|
|
339
|
+
for (int i = 0; i < BatchSize; i++)
|
|
340
|
+
{
|
|
341
|
+
instanceFieldSetter(instance, value);
|
|
342
|
+
sink ^= instance.InstanceField;
|
|
343
|
+
value++;
|
|
344
|
+
count++;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return count;
|
|
348
|
+
}
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
yield return Scenario.Create(
|
|
352
|
+
"Static Field Get (boxed)",
|
|
353
|
+
() =>
|
|
354
|
+
{
|
|
355
|
+
int count = 0;
|
|
356
|
+
for (int i = 0; i < BatchSize; i++)
|
|
357
|
+
{
|
|
358
|
+
sink ^= (int)staticField.GetValue(null);
|
|
359
|
+
count++;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
return count;
|
|
363
|
+
},
|
|
364
|
+
() =>
|
|
365
|
+
{
|
|
366
|
+
int count = 0;
|
|
367
|
+
for (int i = 0; i < BatchSize; i++)
|
|
368
|
+
{
|
|
369
|
+
sink ^= (int)staticFieldGetter();
|
|
370
|
+
count++;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
return count;
|
|
374
|
+
}
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
yield return Scenario.Create(
|
|
378
|
+
"Static Field Set (boxed)",
|
|
379
|
+
() =>
|
|
380
|
+
{
|
|
381
|
+
int count = 0;
|
|
382
|
+
int value = 0;
|
|
383
|
+
for (int i = 0; i < BatchSize; i++)
|
|
384
|
+
{
|
|
385
|
+
staticField.SetValue(null, value);
|
|
386
|
+
sink ^= ReflectionPerfTarget.StaticField;
|
|
387
|
+
value++;
|
|
388
|
+
count++;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return count;
|
|
392
|
+
},
|
|
393
|
+
() =>
|
|
394
|
+
{
|
|
395
|
+
int count = 0;
|
|
396
|
+
int value = 0;
|
|
397
|
+
for (int i = 0; i < BatchSize; i++)
|
|
398
|
+
{
|
|
399
|
+
staticFieldSetter(value);
|
|
400
|
+
sink ^= ReflectionPerfTarget.StaticField;
|
|
401
|
+
value++;
|
|
402
|
+
count++;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return count;
|
|
406
|
+
}
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
yield return Scenario.Create(
|
|
410
|
+
"Instance Property Get (boxed)",
|
|
411
|
+
() =>
|
|
412
|
+
{
|
|
413
|
+
int count = 0;
|
|
414
|
+
for (int i = 0; i < BatchSize; i++)
|
|
415
|
+
{
|
|
416
|
+
sink ^= (int)instanceProperty.GetValue(instance);
|
|
417
|
+
count++;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
return count;
|
|
421
|
+
},
|
|
422
|
+
() =>
|
|
423
|
+
{
|
|
424
|
+
int count = 0;
|
|
425
|
+
for (int i = 0; i < BatchSize; i++)
|
|
426
|
+
{
|
|
427
|
+
sink ^= (int)instancePropertyGetter(instance);
|
|
428
|
+
count++;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return count;
|
|
432
|
+
}
|
|
433
|
+
);
|
|
434
|
+
|
|
435
|
+
yield return Scenario.Create(
|
|
436
|
+
"Instance Property Set (boxed)",
|
|
437
|
+
() =>
|
|
438
|
+
{
|
|
439
|
+
int count = 0;
|
|
440
|
+
int value = 0;
|
|
441
|
+
for (int i = 0; i < BatchSize; i++)
|
|
442
|
+
{
|
|
443
|
+
instanceProperty.SetValue(instance, value);
|
|
444
|
+
sink ^= instance.InstanceProperty;
|
|
445
|
+
value++;
|
|
446
|
+
count++;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
return count;
|
|
450
|
+
},
|
|
451
|
+
() =>
|
|
452
|
+
{
|
|
453
|
+
int count = 0;
|
|
454
|
+
int value = 0;
|
|
455
|
+
for (int i = 0; i < BatchSize; i++)
|
|
456
|
+
{
|
|
457
|
+
instancePropertySetter(instance, value);
|
|
458
|
+
sink ^= instance.InstanceProperty;
|
|
459
|
+
value++;
|
|
460
|
+
count++;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
return count;
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
yield return Scenario.Create(
|
|
468
|
+
"Static Property Get (boxed)",
|
|
469
|
+
() =>
|
|
470
|
+
{
|
|
471
|
+
int count = 0;
|
|
472
|
+
for (int i = 0; i < BatchSize; i++)
|
|
473
|
+
{
|
|
474
|
+
sink ^= (int)staticProperty.GetValue(null);
|
|
475
|
+
count++;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
return count;
|
|
479
|
+
},
|
|
480
|
+
() =>
|
|
481
|
+
{
|
|
482
|
+
int count = 0;
|
|
483
|
+
for (int i = 0; i < BatchSize; i++)
|
|
484
|
+
{
|
|
485
|
+
sink ^= (int)staticPropertyGetter(null);
|
|
486
|
+
count++;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return count;
|
|
490
|
+
}
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
yield return Scenario.Create(
|
|
494
|
+
"Static Property Set (boxed)",
|
|
495
|
+
() =>
|
|
496
|
+
{
|
|
497
|
+
int count = 0;
|
|
498
|
+
int value = 0;
|
|
499
|
+
for (int i = 0; i < BatchSize; i++)
|
|
500
|
+
{
|
|
501
|
+
staticProperty.SetValue(null, value);
|
|
502
|
+
sink ^= ReflectionPerfTarget.StaticProperty;
|
|
503
|
+
value++;
|
|
504
|
+
count++;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
return count;
|
|
508
|
+
},
|
|
509
|
+
() =>
|
|
510
|
+
{
|
|
511
|
+
int count = 0;
|
|
512
|
+
int value = 0;
|
|
513
|
+
for (int i = 0; i < BatchSize; i++)
|
|
514
|
+
{
|
|
515
|
+
staticPropertySetter(null, value);
|
|
516
|
+
sink ^= ReflectionPerfTarget.StaticProperty;
|
|
517
|
+
value++;
|
|
518
|
+
count++;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return count;
|
|
522
|
+
}
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
yield return Scenario.Create(
|
|
526
|
+
"Instance Method Invoke (boxed)",
|
|
527
|
+
() =>
|
|
528
|
+
{
|
|
529
|
+
int count = 0;
|
|
530
|
+
object[] arguments = { 3, 5 };
|
|
531
|
+
for (int i = 0; i < BatchSize; i++)
|
|
532
|
+
{
|
|
533
|
+
sink ^= (int)instanceMethod.Invoke(instance, arguments);
|
|
534
|
+
count++;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return count;
|
|
538
|
+
},
|
|
539
|
+
() =>
|
|
540
|
+
{
|
|
541
|
+
int count = 0;
|
|
542
|
+
object[] arguments = { 3, 5 };
|
|
543
|
+
for (int i = 0; i < BatchSize; i++)
|
|
544
|
+
{
|
|
545
|
+
sink ^= (int)instanceMethodInvoker(instance, arguments);
|
|
546
|
+
count++;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return count;
|
|
550
|
+
}
|
|
551
|
+
);
|
|
552
|
+
|
|
553
|
+
yield return Scenario.Create(
|
|
554
|
+
"Static Method Invoke (boxed)",
|
|
555
|
+
() =>
|
|
556
|
+
{
|
|
557
|
+
int count = 0;
|
|
558
|
+
object[] arguments = { 3, 5 };
|
|
559
|
+
for (int i = 0; i < BatchSize; i++)
|
|
560
|
+
{
|
|
561
|
+
sink ^= (int)staticMethod.Invoke(null, arguments);
|
|
562
|
+
count++;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
return count;
|
|
566
|
+
},
|
|
567
|
+
() =>
|
|
568
|
+
{
|
|
569
|
+
int count = 0;
|
|
570
|
+
object[] arguments = { 3, 5 };
|
|
571
|
+
for (int i = 0; i < BatchSize; i++)
|
|
572
|
+
{
|
|
573
|
+
sink ^= (int)staticMethodInvoker(arguments);
|
|
574
|
+
count++;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
return count;
|
|
578
|
+
}
|
|
579
|
+
);
|
|
580
|
+
|
|
581
|
+
yield return Scenario.Create(
|
|
582
|
+
"Constructor Invoke (boxed)",
|
|
583
|
+
() =>
|
|
584
|
+
{
|
|
585
|
+
int count = 0;
|
|
586
|
+
object[] arguments = { 9 };
|
|
587
|
+
for (int i = 0; i < BatchSize; i++)
|
|
588
|
+
{
|
|
589
|
+
ReflectionPerfTarget created = (ReflectionPerfTarget)
|
|
590
|
+
constructor.Invoke(arguments);
|
|
591
|
+
sink ^= created.InstanceField;
|
|
592
|
+
count++;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return count;
|
|
596
|
+
},
|
|
597
|
+
() =>
|
|
598
|
+
{
|
|
599
|
+
int count = 0;
|
|
600
|
+
object[] arguments = { 9 };
|
|
601
|
+
for (int i = 0; i < BatchSize; i++)
|
|
602
|
+
{
|
|
603
|
+
ReflectionPerfTarget created = (ReflectionPerfTarget)constructorInvoker(
|
|
604
|
+
arguments
|
|
605
|
+
);
|
|
606
|
+
sink ^= created.InstanceField;
|
|
607
|
+
count++;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
return count;
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
private sealed class StrategyConfig
|
|
616
|
+
{
|
|
617
|
+
internal StrategyConfig(
|
|
618
|
+
string label,
|
|
619
|
+
bool? expressionsOverride,
|
|
620
|
+
bool? dynamicIlOverride
|
|
621
|
+
)
|
|
622
|
+
{
|
|
623
|
+
Label = label;
|
|
624
|
+
ExpressionsOverride = expressionsOverride;
|
|
625
|
+
DynamicIlOverride = dynamicIlOverride;
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
internal string Label { get; }
|
|
629
|
+
|
|
630
|
+
internal bool? ExpressionsOverride { get; }
|
|
631
|
+
|
|
632
|
+
internal bool? DynamicIlOverride { get; }
|
|
633
|
+
|
|
634
|
+
internal bool RequiresOverride =>
|
|
635
|
+
ExpressionsOverride.HasValue || DynamicIlOverride.HasValue;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
private sealed class StrategyRunResult
|
|
639
|
+
{
|
|
640
|
+
private StrategyRunResult(
|
|
641
|
+
string label,
|
|
642
|
+
bool supported,
|
|
643
|
+
List<ScenarioResult> boxed,
|
|
644
|
+
List<ScenarioResult> typed,
|
|
645
|
+
string skipReason
|
|
646
|
+
)
|
|
647
|
+
{
|
|
648
|
+
Label = label;
|
|
649
|
+
Supported = supported;
|
|
650
|
+
BoxedResults = boxed ?? new List<ScenarioResult>();
|
|
651
|
+
TypedResults = typed ?? new List<ScenarioResult>();
|
|
652
|
+
SkipReason = skipReason;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
internal string Label { get; }
|
|
656
|
+
|
|
657
|
+
internal bool Supported { get; }
|
|
658
|
+
|
|
659
|
+
internal IReadOnlyList<ScenarioResult> BoxedResults { get; }
|
|
660
|
+
|
|
661
|
+
internal IReadOnlyList<ScenarioResult> TypedResults { get; }
|
|
662
|
+
|
|
663
|
+
internal string SkipReason { get; }
|
|
664
|
+
|
|
665
|
+
internal static StrategyRunResult Create(
|
|
666
|
+
string label,
|
|
667
|
+
List<ScenarioResult> boxed,
|
|
668
|
+
List<ScenarioResult> typed
|
|
669
|
+
)
|
|
670
|
+
{
|
|
671
|
+
return new StrategyRunResult(label, true, boxed, typed, null);
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
internal static StrategyRunResult CreateUnsupported(string label, string reason)
|
|
675
|
+
{
|
|
676
|
+
return new StrategyRunResult(label, false, null, null, reason);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
private static IEnumerable<Scenario> CreateTypedScenarios(ReflectionPerfTarget instance)
|
|
681
|
+
{
|
|
682
|
+
Type targetType = typeof(ReflectionPerfTarget);
|
|
683
|
+
FieldInfo instanceField = targetType.GetField(
|
|
684
|
+
nameof(ReflectionPerfTarget.InstanceField)
|
|
685
|
+
);
|
|
686
|
+
FieldInfo staticField = targetType.GetField(nameof(ReflectionPerfTarget.StaticField));
|
|
687
|
+
PropertyInfo instanceProperty = targetType.GetProperty(
|
|
688
|
+
nameof(ReflectionPerfTarget.InstanceProperty)
|
|
689
|
+
);
|
|
690
|
+
PropertyInfo staticProperty = targetType.GetProperty(
|
|
691
|
+
nameof(ReflectionPerfTarget.StaticProperty)
|
|
692
|
+
);
|
|
693
|
+
MethodInfo instanceMethod = targetType.GetMethod(nameof(ReflectionPerfTarget.Combine));
|
|
694
|
+
MethodInfo staticMethod = targetType.GetMethod(
|
|
695
|
+
nameof(ReflectionPerfTarget.StaticCombine)
|
|
696
|
+
);
|
|
697
|
+
|
|
698
|
+
if (
|
|
699
|
+
instanceField == null
|
|
700
|
+
|| staticField == null
|
|
701
|
+
|| instanceProperty == null
|
|
702
|
+
|| staticProperty == null
|
|
703
|
+
|| instanceMethod == null
|
|
704
|
+
|| staticMethod == null
|
|
705
|
+
)
|
|
706
|
+
{
|
|
707
|
+
throw new InvalidOperationException("ReflectionPerfTarget members not found.");
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
Func<ReflectionPerfTarget, int> instanceFieldGetter = ReflectionHelpers.GetFieldGetter<
|
|
711
|
+
ReflectionPerfTarget,
|
|
712
|
+
int
|
|
713
|
+
>(instanceField);
|
|
714
|
+
FieldSetter<ReflectionPerfTarget, int> instanceFieldSetter =
|
|
715
|
+
ReflectionHelpers.GetFieldSetter<ReflectionPerfTarget, int>(instanceField);
|
|
716
|
+
Func<int> staticFieldGetter = ReflectionHelpers.GetStaticFieldGetter<int>(staticField);
|
|
717
|
+
Action<int> staticFieldSetter = ReflectionHelpers.GetStaticFieldSetter<int>(
|
|
718
|
+
staticField
|
|
719
|
+
);
|
|
720
|
+
Func<ReflectionPerfTarget, int> instancePropertyGetter =
|
|
721
|
+
ReflectionHelpers.GetPropertyGetter<ReflectionPerfTarget, int>(instanceProperty);
|
|
722
|
+
Action<ReflectionPerfTarget, int> instancePropertySetter =
|
|
723
|
+
ReflectionHelpers.GetPropertySetter<ReflectionPerfTarget, int>(instanceProperty);
|
|
724
|
+
Func<int> staticPropertyGetter = ReflectionHelpers.GetStaticPropertyGetter<int>(
|
|
725
|
+
staticProperty
|
|
726
|
+
);
|
|
727
|
+
Action<int> staticPropertySetter = ReflectionHelpers.GetStaticPropertySetter<int>(
|
|
728
|
+
staticProperty
|
|
729
|
+
);
|
|
730
|
+
Func<ReflectionPerfTarget, int, int, int> instanceMethodInvoker =
|
|
731
|
+
ReflectionHelpers.GetInstanceMethodInvoker<ReflectionPerfTarget, int, int, int>(
|
|
732
|
+
instanceMethod
|
|
733
|
+
);
|
|
734
|
+
Func<int, int, int> staticMethodInvoker = ReflectionHelpers.GetStaticMethodInvoker<
|
|
735
|
+
int,
|
|
736
|
+
int,
|
|
737
|
+
int
|
|
738
|
+
>(staticMethod);
|
|
739
|
+
|
|
740
|
+
yield return Scenario.Create(
|
|
741
|
+
"Instance Field Get (typed)",
|
|
742
|
+
() =>
|
|
743
|
+
{
|
|
744
|
+
int count = 0;
|
|
745
|
+
for (int i = 0; i < BatchSize; i++)
|
|
746
|
+
{
|
|
747
|
+
sink ^= instance.InstanceField;
|
|
748
|
+
count++;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
return count;
|
|
752
|
+
},
|
|
753
|
+
() =>
|
|
754
|
+
{
|
|
755
|
+
int count = 0;
|
|
756
|
+
for (int i = 0; i < BatchSize; i++)
|
|
757
|
+
{
|
|
758
|
+
sink ^= instanceFieldGetter(instance);
|
|
759
|
+
count++;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
return count;
|
|
763
|
+
}
|
|
764
|
+
);
|
|
765
|
+
|
|
766
|
+
yield return Scenario.Create(
|
|
767
|
+
"Instance Field Set (typed)",
|
|
768
|
+
() =>
|
|
769
|
+
{
|
|
770
|
+
int count = 0;
|
|
771
|
+
int value = 0;
|
|
772
|
+
for (int i = 0; i < BatchSize; i++)
|
|
773
|
+
{
|
|
774
|
+
instance.InstanceField = value;
|
|
775
|
+
sink ^= instance.InstanceField;
|
|
776
|
+
value++;
|
|
777
|
+
count++;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
return count;
|
|
781
|
+
},
|
|
782
|
+
() =>
|
|
783
|
+
{
|
|
784
|
+
int count = 0;
|
|
785
|
+
int value = 0;
|
|
786
|
+
ReflectionPerfTarget target = instance;
|
|
787
|
+
for (int i = 0; i < BatchSize; i++)
|
|
788
|
+
{
|
|
789
|
+
instanceFieldSetter(ref target, value);
|
|
790
|
+
sink ^= target.InstanceField;
|
|
791
|
+
value++;
|
|
792
|
+
count++;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
return count;
|
|
796
|
+
}
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
yield return Scenario.Create(
|
|
800
|
+
"Static Field Get (typed)",
|
|
801
|
+
() =>
|
|
802
|
+
{
|
|
803
|
+
int count = 0;
|
|
804
|
+
for (int i = 0; i < BatchSize; i++)
|
|
805
|
+
{
|
|
806
|
+
sink ^= ReflectionPerfTarget.StaticField;
|
|
807
|
+
count++;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return count;
|
|
811
|
+
},
|
|
812
|
+
() =>
|
|
813
|
+
{
|
|
814
|
+
int count = 0;
|
|
815
|
+
for (int i = 0; i < BatchSize; i++)
|
|
816
|
+
{
|
|
817
|
+
sink ^= staticFieldGetter();
|
|
818
|
+
count++;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
return count;
|
|
822
|
+
}
|
|
823
|
+
);
|
|
824
|
+
|
|
825
|
+
yield return Scenario.Create(
|
|
826
|
+
"Static Field Set (typed)",
|
|
827
|
+
() =>
|
|
828
|
+
{
|
|
829
|
+
int count = 0;
|
|
830
|
+
int value = 0;
|
|
831
|
+
for (int i = 0; i < BatchSize; i++)
|
|
832
|
+
{
|
|
833
|
+
ReflectionPerfTarget.StaticField = value;
|
|
834
|
+
sink ^= ReflectionPerfTarget.StaticField;
|
|
835
|
+
value++;
|
|
836
|
+
count++;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
return count;
|
|
840
|
+
},
|
|
841
|
+
() =>
|
|
842
|
+
{
|
|
843
|
+
int count = 0;
|
|
844
|
+
int value = 0;
|
|
845
|
+
for (int i = 0; i < BatchSize; i++)
|
|
846
|
+
{
|
|
847
|
+
staticFieldSetter(value);
|
|
848
|
+
sink ^= ReflectionPerfTarget.StaticField;
|
|
849
|
+
value++;
|
|
850
|
+
count++;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
return count;
|
|
854
|
+
}
|
|
855
|
+
);
|
|
856
|
+
|
|
857
|
+
yield return Scenario.Create(
|
|
858
|
+
"Instance Property Get (typed)",
|
|
859
|
+
() =>
|
|
860
|
+
{
|
|
861
|
+
int count = 0;
|
|
862
|
+
for (int i = 0; i < BatchSize; i++)
|
|
863
|
+
{
|
|
864
|
+
sink ^= instance.InstanceProperty;
|
|
865
|
+
count++;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return count;
|
|
869
|
+
},
|
|
870
|
+
() =>
|
|
871
|
+
{
|
|
872
|
+
int count = 0;
|
|
873
|
+
for (int i = 0; i < BatchSize; i++)
|
|
874
|
+
{
|
|
875
|
+
sink ^= instancePropertyGetter(instance);
|
|
876
|
+
count++;
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
return count;
|
|
880
|
+
}
|
|
881
|
+
);
|
|
882
|
+
|
|
883
|
+
yield return Scenario.Create(
|
|
884
|
+
"Instance Property Set (typed)",
|
|
885
|
+
() =>
|
|
886
|
+
{
|
|
887
|
+
int count = 0;
|
|
888
|
+
int value = 0;
|
|
889
|
+
for (int i = 0; i < BatchSize; i++)
|
|
890
|
+
{
|
|
891
|
+
instance.InstanceProperty = value;
|
|
892
|
+
sink ^= instance.InstanceProperty;
|
|
893
|
+
value++;
|
|
894
|
+
count++;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
return count;
|
|
898
|
+
},
|
|
899
|
+
() =>
|
|
900
|
+
{
|
|
901
|
+
int count = 0;
|
|
902
|
+
int value = 0;
|
|
903
|
+
for (int i = 0; i < BatchSize; i++)
|
|
904
|
+
{
|
|
905
|
+
instancePropertySetter(instance, value);
|
|
906
|
+
sink ^= instance.InstanceProperty;
|
|
907
|
+
value++;
|
|
908
|
+
count++;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
return count;
|
|
912
|
+
}
|
|
913
|
+
);
|
|
914
|
+
|
|
915
|
+
yield return Scenario.Create(
|
|
916
|
+
"Static Property Get (typed)",
|
|
917
|
+
() =>
|
|
918
|
+
{
|
|
919
|
+
int count = 0;
|
|
920
|
+
for (int i = 0; i < BatchSize; i++)
|
|
921
|
+
{
|
|
922
|
+
sink ^= ReflectionPerfTarget.StaticProperty;
|
|
923
|
+
count++;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
return count;
|
|
927
|
+
},
|
|
928
|
+
() =>
|
|
929
|
+
{
|
|
930
|
+
int count = 0;
|
|
931
|
+
for (int i = 0; i < BatchSize; i++)
|
|
932
|
+
{
|
|
933
|
+
sink ^= staticPropertyGetter();
|
|
934
|
+
count++;
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
return count;
|
|
938
|
+
}
|
|
939
|
+
);
|
|
940
|
+
|
|
941
|
+
yield return Scenario.Create(
|
|
942
|
+
"Static Property Set (typed)",
|
|
943
|
+
() =>
|
|
944
|
+
{
|
|
945
|
+
int count = 0;
|
|
946
|
+
int value = 0;
|
|
947
|
+
for (int i = 0; i < BatchSize; i++)
|
|
948
|
+
{
|
|
949
|
+
ReflectionPerfTarget.StaticProperty = value;
|
|
950
|
+
sink ^= ReflectionPerfTarget.StaticProperty;
|
|
951
|
+
value++;
|
|
952
|
+
count++;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
return count;
|
|
956
|
+
},
|
|
957
|
+
() =>
|
|
958
|
+
{
|
|
959
|
+
int count = 0;
|
|
960
|
+
int value = 0;
|
|
961
|
+
for (int i = 0; i < BatchSize; i++)
|
|
962
|
+
{
|
|
963
|
+
staticPropertySetter(value);
|
|
964
|
+
sink ^= ReflectionPerfTarget.StaticProperty;
|
|
965
|
+
value++;
|
|
966
|
+
count++;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
return count;
|
|
970
|
+
}
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
yield return Scenario.Create(
|
|
974
|
+
"Instance Method Invoke (typed)",
|
|
975
|
+
() =>
|
|
976
|
+
{
|
|
977
|
+
int count = 0;
|
|
978
|
+
for (int i = 0; i < BatchSize; i++)
|
|
979
|
+
{
|
|
980
|
+
sink ^= instance.Combine(3, 5);
|
|
981
|
+
count++;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return count;
|
|
985
|
+
},
|
|
986
|
+
() =>
|
|
987
|
+
{
|
|
988
|
+
int count = 0;
|
|
989
|
+
for (int i = 0; i < BatchSize; i++)
|
|
990
|
+
{
|
|
991
|
+
sink ^= instanceMethodInvoker(instance, 3, 5);
|
|
992
|
+
count++;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
return count;
|
|
996
|
+
}
|
|
997
|
+
);
|
|
998
|
+
|
|
999
|
+
yield return Scenario.Create(
|
|
1000
|
+
"Static Method Invoke (typed)",
|
|
1001
|
+
() =>
|
|
1002
|
+
{
|
|
1003
|
+
int count = 0;
|
|
1004
|
+
for (int i = 0; i < BatchSize; i++)
|
|
1005
|
+
{
|
|
1006
|
+
sink ^= ReflectionPerfTarget.StaticCombine(3, 5);
|
|
1007
|
+
count++;
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
return count;
|
|
1011
|
+
},
|
|
1012
|
+
() =>
|
|
1013
|
+
{
|
|
1014
|
+
int count = 0;
|
|
1015
|
+
for (int i = 0; i < BatchSize; i++)
|
|
1016
|
+
{
|
|
1017
|
+
sink ^= staticMethodInvoker(3, 5);
|
|
1018
|
+
count++;
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return count;
|
|
1022
|
+
}
|
|
1023
|
+
);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
private static ScenarioResult RunScenario(Scenario scenario)
|
|
1027
|
+
{
|
|
1028
|
+
scenario.Warmup();
|
|
1029
|
+
|
|
1030
|
+
double helperOpsPerSecond = MeasureOpsPerSecond(scenario.Helper);
|
|
1031
|
+
double baselineOpsPerSecond = MeasureOpsPerSecond(scenario.Baseline);
|
|
1032
|
+
|
|
1033
|
+
double speedup =
|
|
1034
|
+
baselineOpsPerSecond <= 0.0
|
|
1035
|
+
? double.PositiveInfinity
|
|
1036
|
+
: helperOpsPerSecond / baselineOpsPerSecond;
|
|
1037
|
+
|
|
1038
|
+
return new ScenarioResult(
|
|
1039
|
+
scenario.Name,
|
|
1040
|
+
helperOpsPerSecond,
|
|
1041
|
+
baselineOpsPerSecond,
|
|
1042
|
+
speedup
|
|
1043
|
+
);
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
private static double MeasureOpsPerSecond(Func<int> executeBatch)
|
|
1047
|
+
{
|
|
1048
|
+
if (executeBatch == null)
|
|
1049
|
+
{
|
|
1050
|
+
return 0.0;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
Stopwatch stopwatch = Stopwatch.StartNew();
|
|
1054
|
+
long operations = 0;
|
|
1055
|
+
|
|
1056
|
+
do
|
|
1057
|
+
{
|
|
1058
|
+
operations += executeBatch();
|
|
1059
|
+
} while (stopwatch.Elapsed < BenchmarkDuration);
|
|
1060
|
+
|
|
1061
|
+
double elapsedSeconds = stopwatch.Elapsed.TotalSeconds;
|
|
1062
|
+
if (elapsedSeconds <= 0.0)
|
|
1063
|
+
{
|
|
1064
|
+
elapsedSeconds = 1.0 / Stopwatch.Frequency;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
return operations / elapsedSeconds;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
private static string FormatOps(double value)
|
|
1071
|
+
{
|
|
1072
|
+
const double Million = 1_000_000.0;
|
|
1073
|
+
const double Thousand = 1_000.0;
|
|
1074
|
+
|
|
1075
|
+
if (value >= Million)
|
|
1076
|
+
{
|
|
1077
|
+
return string.Format(
|
|
1078
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
1079
|
+
"{0:F2}M",
|
|
1080
|
+
value / Million
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
if (value >= Thousand)
|
|
1085
|
+
{
|
|
1086
|
+
return string.Format(
|
|
1087
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
1088
|
+
"{0:F1}K",
|
|
1089
|
+
value / Thousand
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
return string.Format(
|
|
1094
|
+
System.Globalization.CultureInfo.InvariantCulture,
|
|
1095
|
+
"{0:F0}",
|
|
1096
|
+
value
|
|
1097
|
+
);
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
private static string GetOsToken()
|
|
1101
|
+
{
|
|
1102
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
1103
|
+
{
|
|
1104
|
+
return "WINDOWS";
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
1108
|
+
{
|
|
1109
|
+
return "MACOS";
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
1113
|
+
{
|
|
1114
|
+
return "LINUX";
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
return "UNKNOWN";
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
private static string GetScenarioKey(string scenarioName)
|
|
1121
|
+
{
|
|
1122
|
+
if (string.IsNullOrEmpty(scenarioName))
|
|
1123
|
+
{
|
|
1124
|
+
return string.Empty;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
const string BoxedSuffix = " (boxed)";
|
|
1128
|
+
const string TypedSuffix = " (typed)";
|
|
1129
|
+
|
|
1130
|
+
if (scenarioName.EndsWith(BoxedSuffix, StringComparison.Ordinal))
|
|
1131
|
+
{
|
|
1132
|
+
return scenarioName.Substring(0, scenarioName.Length - BoxedSuffix.Length);
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
if (scenarioName.EndsWith(TypedSuffix, StringComparison.Ordinal))
|
|
1136
|
+
{
|
|
1137
|
+
return scenarioName.Substring(0, scenarioName.Length - TypedSuffix.Length);
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
return scenarioName;
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
private sealed class Scenario
|
|
1144
|
+
{
|
|
1145
|
+
private Scenario(string name, Func<int> baseline, Func<int> helper)
|
|
1146
|
+
{
|
|
1147
|
+
Name = name;
|
|
1148
|
+
Baseline = baseline;
|
|
1149
|
+
Helper = helper;
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
public string Name { get; }
|
|
1153
|
+
|
|
1154
|
+
public Func<int> Baseline { get; }
|
|
1155
|
+
|
|
1156
|
+
public Func<int> Helper { get; }
|
|
1157
|
+
|
|
1158
|
+
public static Scenario Create(string name, Func<int> baseline, Func<int> helper)
|
|
1159
|
+
{
|
|
1160
|
+
if (string.IsNullOrEmpty(name))
|
|
1161
|
+
{
|
|
1162
|
+
throw new ArgumentException("Scenario name must be provided.", nameof(name));
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (baseline == null)
|
|
1166
|
+
{
|
|
1167
|
+
throw new ArgumentNullException(nameof(baseline));
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
if (helper == null)
|
|
1171
|
+
{
|
|
1172
|
+
throw new ArgumentNullException(nameof(helper));
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
return new Scenario(name, baseline, helper);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
public void Warmup()
|
|
1179
|
+
{
|
|
1180
|
+
_ = Baseline();
|
|
1181
|
+
_ = Helper();
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
private readonly struct ScenarioResult
|
|
1186
|
+
{
|
|
1187
|
+
public ScenarioResult(
|
|
1188
|
+
string name,
|
|
1189
|
+
double helperOpsPerSecond,
|
|
1190
|
+
double baselineOpsPerSecond,
|
|
1191
|
+
double speedup
|
|
1192
|
+
)
|
|
1193
|
+
{
|
|
1194
|
+
Name = name;
|
|
1195
|
+
HelperOpsPerSecond = helperOpsPerSecond;
|
|
1196
|
+
BaselineOpsPerSecond = baselineOpsPerSecond;
|
|
1197
|
+
Speedup = speedup;
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
public string Name { get; }
|
|
1201
|
+
|
|
1202
|
+
public double HelperOpsPerSecond { get; }
|
|
1203
|
+
|
|
1204
|
+
public double BaselineOpsPerSecond { get; }
|
|
1205
|
+
|
|
1206
|
+
public double Speedup { get; }
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
private sealed class ReflectionPerfTarget
|
|
1210
|
+
{
|
|
1211
|
+
public static int StaticField;
|
|
1212
|
+
|
|
1213
|
+
public static int StaticProperty { get; set; }
|
|
1214
|
+
|
|
1215
|
+
public int InstanceField;
|
|
1216
|
+
|
|
1217
|
+
public int InstanceProperty { get; set; }
|
|
1218
|
+
|
|
1219
|
+
public ReflectionPerfTarget() { }
|
|
1220
|
+
|
|
1221
|
+
public ReflectionPerfTarget(int value)
|
|
1222
|
+
{
|
|
1223
|
+
InstanceField = value;
|
|
1224
|
+
InstanceProperty = value;
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
public int Combine(int first, int second)
|
|
1228
|
+
{
|
|
1229
|
+
return InstanceField + first + second;
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
public static int StaticCombine(int first, int second)
|
|
1233
|
+
{
|
|
1234
|
+
return StaticField + first + second;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
}
|