com.wallstop-studios.dxmessaging 2.0.0-rc13 → 2.0.0-rc15

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.
@@ -14,10 +14,12 @@
14
14
  }
15
15
 
16
16
  /// <summary>
17
- /// Debug functionality for all of the Messaging Components.
17
+ /// Debug functionality for all the Messaging Components.
18
18
  /// </summary>
19
19
  public static class MessagingDebug
20
20
  {
21
+ public static bool enabled = false;
22
+
21
23
  /// <summary>
22
24
  /// Custom log function to use.
23
25
  /// </summary>
@@ -27,7 +29,7 @@
27
29
  public static Action<LogLevel, string> LogFunction = null;
28
30
 
29
31
  /// <summary>
30
- /// Logs a message to the debug log function, if it's not null.
32
+ /// Logs a message to the debug log function if it's not null.
31
33
  /// </summary>
32
34
  /// <param name="logLevel">Severity of the message.</param>
33
35
  /// <param name="message">Message to log.</param>
@@ -47,6 +49,11 @@
47
49
  /// <param name="args">Args to populate format string with.</param>
48
50
  public static void Log(LogLevel logLevel, string message, params object[] args)
49
51
  {
52
+ if (!enabled)
53
+ {
54
+ return;
55
+ }
56
+
50
57
  Action<LogLevel, string> logFunction = LogFunction;
51
58
  /*
52
59
  We can potentially avoid an unnecessary string.Format call if the LogFunction is null,
@@ -6,59 +6,30 @@
6
6
  using DxMessaging.Core;
7
7
  using DxMessaging.Core.Extensions;
8
8
  using global::Unity.PerformanceTesting;
9
- using global::Unity.Profiling;
10
9
  using NUnit.Framework;
11
10
  using Scripts.Components;
12
11
  using Scripts.Messages;
13
12
  using UnityEngine;
14
- using UnityEngine.Profiling;
13
+ using UnityEngine.TestTools.Constraints;
15
14
  using Debug = UnityEngine.Debug;
15
+ using Is = NUnit.Framework.Is;
16
16
  using Object = UnityEngine.Object;
17
17
 
18
18
  public sealed class PerformanceTests : MessagingTestBase
19
19
  {
20
- internal static string FormatBytes(long bytes)
21
- {
22
- if (bytes < 1024)
23
- {
24
- return $"{bytes} B";
25
- }
26
-
27
- string[] sizes = { "KB", "MB", "GB", "TB", "PB", "EB" };
28
- double len = bytes;
29
- int order = 0;
30
-
31
- while (1024 <= len && order < sizes.Length - 1)
32
- {
33
- len /= 1024;
34
- order++;
35
- }
36
-
37
- return $"{len:0.##} {sizes[order]}";
38
- }
39
-
40
- [SetUp]
41
- public override void Setup()
42
- {
43
- base.Setup();
44
- MessagingDebug.LogFunction = (_, _) => { };
45
- }
20
+ protected override bool MessagingDebugEnabled => false;
46
21
 
47
22
  [Test]
48
23
  public void Benchmark()
49
24
  {
50
25
  TimeSpan timeout = TimeSpan.FromSeconds(2);
51
26
 
52
- Debug.Log("| Message Tech | Operations / Second | Memory Allocated |");
53
- Debug.Log("| ------------ | ------------------- | --------------- |");
27
+ Debug.Log("| Message Tech | Operations / Second | Allocations? |");
28
+ Debug.Log("| ------------ | ------------------- | ------------ | ");
54
29
 
55
30
  ComplexTargetedMessage message = new(Guid.NewGuid());
56
31
  Stopwatch timer = Stopwatch.StartNew();
57
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
58
- ProfilerCategory.Memory,
59
- "GC.Alloc"
60
- );
61
- recorder.Stop();
32
+
62
33
  RunTest(component => Unity(timer, timeout, component.gameObject, message));
63
34
  RunTest(component => NormalGameObject(timer, timeout, component, message));
64
35
  RunTest(component => NormalComponent(timer, timeout, component, message));
@@ -77,16 +48,19 @@
77
48
  MessageRegistrationToken token = GetToken(
78
49
  go.GetComponent<EmptyMessageAwareComponent>()
79
50
  );
51
+ // ReSharper disable once NotAccessedVariable
80
52
  int count = 0;
81
53
  token.RegisterGameObjectBroadcast<SimpleBroadcastMessage>(go, Handle);
82
54
 
83
55
  SimpleBroadcastMessage message = new();
56
+ SampleGroup time = new("Time", SampleUnit.Nanosecond);
84
57
 
85
58
  Measure
86
59
  .Method(() => message.EmitGameObjectBroadcast(go))
60
+ .SampleGroup(time)
87
61
  .WarmupCount(10)
88
- .IterationsPerMeasurement(10)
89
- .MeasurementCount(1_000)
62
+ .IterationsPerMeasurement(1)
63
+ .MeasurementCount(10_000)
90
64
  .Run();
91
65
  return;
92
66
 
@@ -104,16 +78,19 @@
104
78
  MessageRegistrationToken token = GetToken(
105
79
  go.GetComponent<EmptyMessageAwareComponent>()
106
80
  );
81
+ // ReSharper disable once NotAccessedVariable
107
82
  int count = 0;
108
83
  token.RegisterGameObjectTargeted<SimpleTargetedMessage>(go, Handle);
109
84
 
110
85
  SimpleTargetedMessage message = new();
86
+ SampleGroup time = new("Time", SampleUnit.Nanosecond);
111
87
 
112
88
  Measure
113
89
  .Method(() => message.EmitGameObjectTargeted(go))
90
+ .SampleGroup(time)
114
91
  .WarmupCount(10)
115
- .IterationsPerMeasurement(10)
116
- .MeasurementCount(1_000)
92
+ .IterationsPerMeasurement(1)
93
+ .MeasurementCount(10_000)
117
94
  .Run();
118
95
  return;
119
96
 
@@ -131,16 +108,19 @@
131
108
  MessageRegistrationToken token = GetToken(
132
109
  go.GetComponent<EmptyMessageAwareComponent>()
133
110
  );
111
+ // ReSharper disable once NotAccessedVariable
134
112
  int count = 0;
135
113
  token.RegisterUntargeted<SimpleUntargetedMessage>(Handle);
136
114
 
137
115
  SimpleUntargetedMessage message = new();
116
+ SampleGroup time = new("Time", SampleUnit.Nanosecond);
138
117
 
139
118
  Measure
140
119
  .Method(() => message.EmitUntargeted())
120
+ .SampleGroup(time)
141
121
  .WarmupCount(10)
142
- .IterationsPerMeasurement(10)
143
- .MeasurementCount(1_000)
122
+ .IterationsPerMeasurement(1)
123
+ .MeasurementCount(10_000)
144
124
  .Run();
145
125
  return;
146
126
 
@@ -169,11 +149,11 @@
169
149
  string testName,
170
150
  int count,
171
151
  TimeSpan timeout,
172
- long memoryUsed
152
+ bool allocating
173
153
  )
174
154
  {
175
155
  Debug.Log(
176
- $"| {testName} | {Math.Floor(count / timeout.TotalSeconds):N0} | {FormatBytes(Math.Max(memoryUsed, 0))} |"
156
+ $"| {testName} | {Math.Floor(count / timeout.TotalSeconds):N0} | {(allocating ? "Yes" : "No")} |"
177
157
  );
178
158
  }
179
159
 
@@ -201,10 +181,12 @@
201
181
  int count = 0;
202
182
  var component = target.AddComponent<SimpleMessageAwareComponent>();
203
183
  component.slowComplexTargetedHandler = () => ++count;
204
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
205
- ProfilerCategory.Memory,
206
- "GC.Alloc"
184
+ // Pre-warm
185
+ target.SendMessage(
186
+ nameof(SimpleMessageAwareComponent.HandleSlowComplexTargetedMessage),
187
+ message
207
188
  );
189
+
208
190
  timer.Restart();
209
191
  do
210
192
  {
@@ -213,8 +195,25 @@
213
195
  message
214
196
  );
215
197
  } while (timer.Elapsed < timeout);
216
- recorder.Stop();
217
- DisplayCount("Unity", count, timeout, recorder.LastValue);
198
+
199
+ bool allocating;
200
+ try
201
+ {
202
+ Assert.That(
203
+ () =>
204
+ target.SendMessage(
205
+ nameof(SimpleMessageAwareComponent.HandleSlowComplexTargetedMessage),
206
+ message
207
+ ),
208
+ Is.Not.AllocatingGCMemory()
209
+ );
210
+ allocating = false;
211
+ }
212
+ catch
213
+ {
214
+ allocating = true;
215
+ }
216
+ DisplayCount("Unity", count, timeout, allocating);
218
217
  }
219
218
 
220
219
  private void NormalGameObject(
@@ -229,18 +228,26 @@
229
228
 
230
229
  GameObject go = component.gameObject;
231
230
  token.RegisterGameObjectTargeted<ComplexTargetedMessage>(go, Handle);
232
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
233
- ProfilerCategory.Memory,
234
- "GC.Alloc"
235
- );
231
+ // Pre-warm
232
+ message.EmitGameObjectTargeted(go);
233
+
236
234
  timer.Restart();
237
235
  do
238
236
  {
239
237
  message.EmitGameObjectTargeted(go);
240
238
  } while (timer.Elapsed < timeout);
239
+ bool allocating;
240
+ try
241
+ {
242
+ Assert.That(() => message.EmitGameObjectTargeted(go), Is.Not.AllocatingGCMemory());
243
+ allocating = false;
244
+ }
245
+ catch
246
+ {
247
+ allocating = true;
248
+ }
241
249
 
242
- recorder.Stop();
243
- DisplayCount("DxMessaging (GameObject) - Normal", count, timeout, recorder.LastValue);
250
+ DisplayCount("DxMessaging (GameObject) - Normal", count, timeout, allocating);
244
251
  return;
245
252
 
246
253
  void Handle(ComplexTargetedMessage _)
@@ -260,17 +267,29 @@
260
267
  var token = GetToken(component);
261
268
 
262
269
  token.RegisterComponentTargeted<ComplexTargetedMessage>(component, Handle);
263
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
264
- ProfilerCategory.Memory,
265
- "GC.Alloc"
266
- );
270
+ // Pre-warm
271
+ message.EmitComponentTargeted(component);
272
+
267
273
  timer.Restart();
268
274
  do
269
275
  {
270
276
  message.EmitComponentTargeted(component);
271
277
  } while (timer.Elapsed < timeout);
272
- recorder.Stop();
273
- DisplayCount("DxMessaging (Component) - Normal", count, timeout, recorder.LastValue);
278
+
279
+ bool allocating;
280
+ try
281
+ {
282
+ Assert.That(
283
+ () => message.EmitComponentTargeted(component),
284
+ Is.Not.AllocatingGCMemory()
285
+ );
286
+ allocating = false;
287
+ }
288
+ catch
289
+ {
290
+ allocating = true;
291
+ }
292
+ DisplayCount("DxMessaging (Component) - Normal", count, timeout, allocating);
274
293
  return;
275
294
 
276
295
  void Handle(ComplexTargetedMessage _)
@@ -291,17 +310,28 @@
291
310
 
292
311
  GameObject go = component.gameObject;
293
312
  token.RegisterGameObjectTargeted<ComplexTargetedMessage>(go, Handle);
294
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
295
- ProfilerCategory.Memory,
296
- "GC.Alloc"
297
- );
313
+ // Pre-warm
314
+ message.EmitGameObjectTargeted(component.gameObject);
315
+
298
316
  timer.Restart();
299
317
  do
300
318
  {
301
319
  message.EmitGameObjectTargeted(component.gameObject);
302
320
  } while (timer.Elapsed < timeout);
303
- recorder.Stop();
304
- DisplayCount("DxMessaging (GameObject) - No-Copy", count, timeout, recorder.LastValue);
321
+ bool allocating;
322
+ try
323
+ {
324
+ Assert.That(
325
+ () => message.EmitGameObjectTargeted(component.gameObject),
326
+ Is.Not.AllocatingGCMemory()
327
+ );
328
+ allocating = false;
329
+ }
330
+ catch
331
+ {
332
+ allocating = true;
333
+ }
334
+ DisplayCount("DxMessaging (GameObject) - No-Copy", count, timeout, allocating);
305
335
  return;
306
336
 
307
337
  void Handle(ref ComplexTargetedMessage _)
@@ -321,17 +351,29 @@
321
351
  var token = GetToken(component);
322
352
 
323
353
  token.RegisterComponentTargeted<ComplexTargetedMessage>(component, Handle);
324
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
325
- ProfilerCategory.Memory,
326
- "GC.Alloc"
327
- );
354
+ // Pre-warm
355
+ message.EmitComponentTargeted(component);
356
+
328
357
  timer.Restart();
329
358
  do
330
359
  {
331
360
  message.EmitComponentTargeted(component);
332
361
  } while (timer.Elapsed < timeout);
333
- recorder.Stop();
334
- DisplayCount("DxMessaging (Component) - No-Copy", count, timeout, recorder.LastValue);
362
+
363
+ bool allocating;
364
+ try
365
+ {
366
+ Assert.That(
367
+ () => message.EmitComponentTargeted(component),
368
+ Is.Not.AllocatingGCMemory()
369
+ );
370
+ allocating = false;
371
+ }
372
+ catch
373
+ {
374
+ allocating = true;
375
+ }
376
+ DisplayCount("DxMessaging (Component) - No-Copy", count, timeout, allocating);
335
377
  return;
336
378
 
337
379
  void Handle(ref ComplexTargetedMessage _)
@@ -351,17 +393,26 @@
351
393
  var token = GetToken(component);
352
394
 
353
395
  token.RegisterUntargeted<SimpleUntargetedMessage>(Handle);
354
- ProfilerRecorder recorder = ProfilerRecorder.StartNew(
355
- ProfilerCategory.Memory,
356
- "GC.Alloc"
357
- );
396
+ // Pre-warm
397
+ message.EmitUntargeted();
398
+
358
399
  timer.Restart();
359
400
  do
360
401
  {
361
402
  message.EmitUntargeted();
362
403
  } while (timer.Elapsed < timeout);
363
- recorder.Stop();
364
- DisplayCount("DxMessaging (Untargeted) - No-Copy", count, timeout, recorder.LastValue);
404
+
405
+ bool allocating;
406
+ try
407
+ {
408
+ Assert.That(() => message.EmitUntargeted(), Is.Not.AllocatingGCMemory());
409
+ allocating = false;
410
+ }
411
+ catch
412
+ {
413
+ allocating = true;
414
+ }
415
+ DisplayCount("DxMessaging (Untargeted) - No-Copy", count, timeout, allocating);
365
416
  return;
366
417
 
367
418
  void Handle(ref SimpleUntargetedMessage _)
@@ -22,9 +22,12 @@
22
22
  protected readonly List<GameObject> _spawned = new();
23
23
  protected readonly Random _random = new();
24
24
 
25
+ protected virtual bool MessagingDebugEnabled => true;
26
+
25
27
  [SetUp]
26
28
  public virtual void Setup()
27
29
  {
30
+ MessagingDebug.enabled = MessagingDebugEnabled;
28
31
  MessagingDebug.LogFunction = (level, message) =>
29
32
  {
30
33
  switch (level)
@@ -45,6 +48,18 @@
45
48
  Assert.IsNotNull(messageBus);
46
49
  messageBus.Log.Enabled = true;
47
50
  _numRegistrations = 150;
51
+
52
+ LogMessageBusStatus();
53
+ }
54
+
55
+ protected void LogMessageBusStatus()
56
+ {
57
+ MessageBus messageBus = MessageHandler.MessageBus;
58
+ Debug.Log(
59
+ $"Untargeted registrations: {messageBus.RegisteredUntargeted}, "
60
+ + $"targeted registrations: {messageBus.RegisteredTargeted}, "
61
+ + $"broadcast registrations: {messageBus.RegisteredBroadcast}."
62
+ );
48
63
  }
49
64
 
50
65
  [TearDown]
@@ -154,20 +169,12 @@
154
169
 
155
170
  protected IEnumerator WaitUntilMessageHandlerIsFresh()
156
171
  {
157
- Setup();
158
172
  MessageBus messageBus = MessageHandler.MessageBus;
159
173
  Assert.IsNotNull(messageBus);
160
174
 
161
175
  Stopwatch timer = Stopwatch.StartNew();
162
176
 
163
- bool IsStale()
164
- {
165
- return messageBus.RegisteredUntargeted != 0
166
- || messageBus.RegisteredTargeted != 0
167
- || messageBus.RegisteredBroadcast != 0;
168
- }
169
-
170
- while (IsStale() && timer.Elapsed < TimeSpan.FromSeconds(2.5))
177
+ while (IsStale() && timer.Elapsed < TimeSpan.FromSeconds(1.25))
171
178
  {
172
179
  yield return null;
173
180
  }
@@ -180,6 +187,14 @@
180
187
  messageBus.RegisteredBroadcast,
181
188
  messageBus.Log
182
189
  );
190
+ yield break;
191
+
192
+ bool IsStale()
193
+ {
194
+ return messageBus.RegisteredUntargeted != 0
195
+ || messageBus.RegisteredTargeted != 0
196
+ || messageBus.RegisteredBroadcast != 0;
197
+ }
183
198
  }
184
199
  }
185
200
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "com.wallstop-studios.dxmessaging",
3
- "version": "2.0.0-rc13",
3
+ "version": "2.0.0-rc15",
4
4
  "displayName": "DxMessaging",
5
5
  "description": "Synchronous Event Bus for Unity",
6
6
  "unity": "2021.3",