feat: benchmark

This commit is contained in:
cneicy committed 2026-03-08 17:02:48 +08:00
1 parent f64289406f
commit 7d9e0566dd
5 files changed
+402

No files matched your search

+68
View File
@@ -0,0 +1,68 @@
[Benchmark] 开始:无订阅者触发
[Benchmark] 完成:无订阅者触发
[Benchmark] 开始:单订阅者同步触发
[Benchmark] 完成:单订阅者同步触发
[Benchmark] 开始:30 订阅者同步触发
[Benchmark] 完成:30 订阅者同步触发
[Benchmark] 开始:异步触发
[Benchmark] 完成:异步触发
[Benchmark] 开始:注册/注销
[Benchmark] 完成:RegisterEvent
[Benchmark] 完成:UnregisterEvent
[Benchmark] 开始:EventPool vs new
[Benchmark] 完成:new() 分配
[Benchmark] 完成:Pool.Get()
[Benchmark] 开始:已取消事件跳过
[Benchmark] 完成:已取消事件跳过
╔══════════════════════════════════════════════════╗
║ ShrinkEventBus Benchmark Report ║
║ iter= 1000000 mass=10000 reg= 5000 cancel=100000 ║
╚══════════════════════════════════════════════════╝
▶ TriggerEvent × 无订阅者(基线)
总耗时 : 68.481 ms
单次 : 0.0685 μs/op
吞吐量 : 14602548 ops/s
▶ TriggerEvent × 单订阅者同步
总耗时 : 115.262 ms
单次 : 0.1153 μs/op
吞吐量 : 8675871 ops/s
▶ TriggerEvent × 30 订阅者同步
总耗时 : 7.376 ms
单次 : 0.7376 μs/op
吞吐量 : 1355712 ops/s
▶ TriggerEventAsync × 单订阅者异步
总耗时 : 0.684 ms
单次 : 0.6840 μs/op
吞吐量 : 1461988 ops/s
▶ RegisterEvent
总耗时 : 18.780 ms
单次 : 3.7561 μs/op
吞吐量 : 266236 ops/s
▶ UnregisterEvent
总耗时 : 117.646 ms
单次 : 23.5292 μs/op
吞吐量 : 42500 ops/s
▶ TriggerEvent × new() [GC Gen0=6457]
总耗时 : 7684.313 ms
单次 : 7.6843 μs/op
吞吐量 : 130135 ops/s
▶ TriggerEvent × Pool.Get() [GC Gen0= 91]
总耗时 : 348.948 ms
单次 : 0.3489 μs/op
吞吐量 : 2865761 ops/s
▶ TriggerEvent × 已取消事件跳过(10 订阅者)
总耗时 : 25.282 ms
单次 : 0.2528 μs/op
吞吐量 : 3955352 ops/s
══════════════════════════════════════════════════
全部测试完成
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 87ccd659dba774447b9d6941761b8300
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+4
View File
@@ -15,6 +15,10 @@
| 📦 **对象池** | 内置 `EventPool<T>`,高频事件零 GC |
| 🔍 **调试友好** | Editor 事件查看器实时追踪订阅者与触发日志 |
## 👓Benchmark
[Benchmark结果](Benchmark.txt)
## 📦 依赖
- Unity 2022.3+
+320
View File
@@ -0,0 +1,320 @@
using System;
using System.Diagnostics;
using System.Text;
using Cysharp.Threading.Tasks;
using UnityEngine;
using Debug = UnityEngine.Debug;
namespace ShrinkEventBus.Runtime
{
/// <summary>
/// ShrinkEventBus 性能测试
/// 挂在任意 GameObject 上,运行后在 Console 查看结果。
/// 在进行测试时不要打开事件查看器大的追踪实时事件功能
/// </summary>
public class EventBusBenchmark : MonoBehaviour
{
[Header("同步触发测试")] public int iterations = 1000000;
[Header("大批量订阅者测试")] public int subscriberCount = 30;
public int massIterations = 1000;
[Header("异步触发测试")] public int asyncIterations = 1000;
[Header("注册 / 注销测试")] public int registerIterations = 5000;
[Header("取消事件测试")] public int cancelIterations = 100000;
public class BenchmarkEvent : EventBase
{
public int Value { get; set; }
}
[Cancelable]
public class CancelableBenchmarkEvent : EventBase
{
public int Value { get; set; }
}
private readonly StringBuilder _report = new();
private void Start()
{
RunAllBenchmarks().Forget();
}
private async UniTaskVoid RunAllBenchmarks()
{
_report.Clear();
_report.AppendLine("╔══════════════════════════════════════════════════╗");
_report.AppendLine("║ ShrinkEventBus Benchmark Report ║");
_report.AppendLine(
$"║ iter={iterations,8} mass={massIterations,5} reg={registerIterations,5} cancel={cancelIterations,6} ║");
_report.AppendLine("╚══════════════════════════════════════════════════╝");
Log("开始:无订阅者触发");
Bench_TriggerEvent_NoSubscriber();
await UniTask.Yield();
Log("开始:单订阅者同步触发");
Bench_TriggerEvent_SingleSubscriber();
await UniTask.Yield();
Log($"开始:{subscriberCount} 订阅者同步触发");
Bench_TriggerEvent_MassSubscribers();
await UniTask.Yield();
Log("开始:异步触发");
await Bench_TriggerEventAsync();
await UniTask.Yield();
Log("开始:注册/注销");
Bench_RegisterUnregister();
await UniTask.Yield();
Log("开始:EventPool vs new");
Bench_EventPool_vs_New();
await UniTask.Yield();
Log("开始:已取消事件跳过");
Bench_CanceledEvent_Skip();
_report.AppendLine("\n══════════════════════════════════════════════════");
_report.AppendLine(" 全部测试完成");
Debug.Log(_report.ToString());
}
private void Bench_TriggerEvent_NoSubscriber()
{
EventBus.ClearAllSubscribersForEvent<BenchmarkEvent>();
var evt = new BenchmarkEvent { Value = 1 };
for (var i = 0; i < 500; i++) EventBus.TriggerEvent(evt);
var sw = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++) EventBus.TriggerEvent(evt);
sw.Stop();
Record("TriggerEvent × 无订阅者(基线)", sw.Elapsed.TotalMilliseconds, iterations);
Log("完成:无订阅者触发");
}
private void Bench_TriggerEvent_SingleSubscriber()
{
EventBus.ClearAllSubscribersForEvent<BenchmarkEvent>();
var dummy = 0;
void Handler(BenchmarkEvent e)
{
dummy = e.Value;
}
EventBus.RegisterEvent<BenchmarkEvent>(Handler, EventPriority.NORMAL);
var evt = new BenchmarkEvent { Value = 1 };
for (var i = 0; i < 500; i++) EventBus.TriggerEvent(evt);
var sw = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++) EventBus.TriggerEvent(evt);
sw.Stop();
Record("TriggerEvent × 单订阅者同步", sw.Elapsed.TotalMilliseconds, iterations);
Log("完成:单订阅者同步触发");
EventBus.UnregisterEvent<BenchmarkEvent>(Handler);
GC.KeepAlive(dummy);
}
private void Bench_TriggerEvent_MassSubscribers()
{
EventBus.ClearAllSubscribersForEvent<BenchmarkEvent>();
var dummy = 0;
var handlers = new Action<BenchmarkEvent>[subscriberCount];
for (var i = 0; i < subscriberCount; i++)
{
var captured = i;
handlers[i] = e => { dummy = captured + e.Value; };
EventBus.RegisterEvent<BenchmarkEvent>(handlers[i], EventPriority.NORMAL);
}
var evt = new BenchmarkEvent { Value = 1 };
for (var i = 0; i < 100; i++) EventBus.TriggerEvent(evt);
var sw = Stopwatch.StartNew();
for (var i = 0; i < massIterations; i++) EventBus.TriggerEvent(evt);
sw.Stop();
Record($"TriggerEvent × {subscriberCount} 订阅者同步", sw.Elapsed.TotalMilliseconds, massIterations);
Log($"完成:{subscriberCount} 订阅者同步触发");
foreach (var h in handlers) EventBus.UnregisterEvent<BenchmarkEvent>(h);
GC.KeepAlive(dummy);
}
private async UniTask Bench_TriggerEventAsync()
{
EventBus.ClearAllSubscribersForEvent<BenchmarkEvent>();
var dummy = 0;
async UniTask Handler(BenchmarkEvent e)
{
dummy = e.Value;
await UniTask.CompletedTask;
}
EventBus.RegisterEvent<BenchmarkEvent>(Handler, EventPriority.NORMAL);
var evt = new BenchmarkEvent { Value = 1 };
for (var i = 0; i < 10; i++) await EventBus.TriggerEventAsync(evt);
const int perFrame = 10;
var totalMs = 0.0;
var ran = 0;
while (ran < asyncIterations)
{
await UniTask.Yield();
var count = Math.Min(perFrame, asyncIterations - ran);
var sw = Stopwatch.StartNew();
for (var i = 0; i < count; i++) await EventBus.TriggerEventAsync(evt);
sw.Stop();
totalMs += sw.Elapsed.TotalMilliseconds;
ran += count;
}
Record("TriggerEventAsync × 单订阅者异步", totalMs, asyncIterations);
Log("完成:异步触发");
EventBus.UnregisterEvent<BenchmarkEvent>(Handler);
GC.KeepAlive(dummy);
}
private void Bench_RegisterUnregister()
{
var dummy = 0;
var handlers = new Action<BenchmarkEvent>[registerIterations];
for (var i = 0; i < registerIterations; i++)
{
var captured = i;
handlers[i] = e => { dummy = captured; };
}
var swReg = Stopwatch.StartNew();
for (var i = 0; i < registerIterations; i++)
EventBus.RegisterEvent<BenchmarkEvent>(handlers[i], EventPriority.NORMAL);
swReg.Stop();
Record("RegisterEvent", swReg.Elapsed.TotalMilliseconds, registerIterations);
Log("完成:RegisterEvent");
var swUnreg = Stopwatch.StartNew();
for (var i = 0; i < registerIterations; i++)
EventBus.UnregisterEvent<BenchmarkEvent>(handlers[i]);
swUnreg.Stop();
Record("UnregisterEvent", swUnreg.Elapsed.TotalMilliseconds, registerIterations);
Log("完成:UnregisterEvent");
GC.KeepAlive(dummy);
}
private void Bench_EventPool_vs_New()
{
EventBus.ClearAllSubscribersForEvent<BenchmarkEvent>();
void Handler(BenchmarkEvent e)
{
}
EventBus.RegisterEvent<BenchmarkEvent>(Handler, EventPriority.NORMAL);
for (var i = 0; i < 64; i++)
EventPool<BenchmarkEvent>.Release(EventPool<BenchmarkEvent>.Get());
GC.Collect();
var gcBefore = GC.CollectionCount(0);
var swNew = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
var e = new BenchmarkEvent { Value = i };
EventBus.TriggerEvent(e);
}
swNew.Stop();
var gcNew = GC.CollectionCount(0) - gcBefore;
Record($"TriggerEvent × new() [GC Gen0={gcNew,3}]", swNew.Elapsed.TotalMilliseconds, iterations);
Log("完成:new() 分配");
GC.Collect();
gcBefore = GC.CollectionCount(0);
var swPool = Stopwatch.StartNew();
for (var i = 0; i < iterations; i++)
{
using var e = EventPool<BenchmarkEvent>.Get();
e.Value = i;
EventBus.TriggerEvent(e);
}
swPool.Stop();
var gcPool = GC.CollectionCount(0) - gcBefore;
Record($"TriggerEvent × Pool.Get() [GC Gen0={gcPool,3}]", swPool.Elapsed.TotalMilliseconds, iterations);
Log("完成:Pool.Get()");
EventBus.UnregisterEvent<BenchmarkEvent>(Handler);
}
private void Bench_CanceledEvent_Skip()
{
EventBus.ClearAllSubscribersForEvent<CancelableBenchmarkEvent>();
var dummy = 0;
var handlers = new Action<CancelableBenchmarkEvent>[10];
for (var i = 0; i < 10; i++)
{
handlers[i] = e => { dummy = e.Value; };
EventBus.RegisterEvent<CancelableBenchmarkEvent>(handlers[i], EventPriority.NORMAL);
}
var evt = new CancelableBenchmarkEvent { Value = 1 };
evt.SetCanceled(true);
for (var i = 0; i < 500; i++) EventBus.TriggerEvent(evt);
var sw = Stopwatch.StartNew();
for (var i = 0; i < cancelIterations; i++) EventBus.TriggerEvent(evt);
sw.Stop();
Record("TriggerEvent × 已取消事件跳过(10 订阅者)", sw.Elapsed.TotalMilliseconds, cancelIterations);
Log("完成:已取消事件跳过");
foreach (var h in handlers)
EventBus.UnregisterEvent<CancelableBenchmarkEvent>(h);
GC.KeepAlive(dummy);
}
private static void Log(string msg) =>
Debug.Log($"[Benchmark] {msg}");
private void Record(string label, double totalMs, int count)
{
var perOp = totalMs / count * 1000.0;
var throughput = count / (totalMs / 1000.0);
_report.AppendLine($"\n ▶ {label}");
_report.AppendLine($" 总耗时 : {totalMs,10:F3} ms");
_report.AppendLine($" 单次 : {perOp,10:F4} μs/op");
_report.AppendLine($" 吞吐量 : {throughput,10:F0} ops/s");
}
}
}
+3
View File
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9619a4dc431b4b2c8268c32a9e56f72f
timeCreated: 1772913062