perf(eventbus): remove inline allocations and expose binding snapshots

This commit is contained in:
2026-09-29 10:14:45 +08:00
parent c44a7e6dda
commit 84536203c7
7 changed files with 50 additions and 10 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ namespace ShrinkEventBus.CodeGen
{ {
public override ILPostProcessor GetInstance() => this; public override ILPostProcessor GetInstance() => this;
public override bool WillProcess(ICompiledAssembly compiledAssembly) => // ShrinkRegistryILPostProcessor owns the complete pass, including event bindings.
UnityShrinkCodeGenAdapter.ReferencesAny(compiledAssembly, "ShrinkEventBus.Runtime"); public override bool WillProcess(ICompiledAssembly compiledAssembly) => false;
public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) => public override ILPostProcessResult Process(ICompiledAssembly compiledAssembly) =>
WillProcess(compiledAssembly) WillProcess(compiledAssembly)
+2 -2
View File
@@ -5,7 +5,7 @@
<AssemblyName>ShrinkEventBus.Runtime</AssemblyName> <AssemblyName>ShrinkEventBus.Runtime</AssemblyName>
<RootNamespace>ShrinkEventBus</RootNamespace> <RootNamespace>ShrinkEventBus</RootNamespace>
<PackageId>ShrinkSDK.EventBus</PackageId> <PackageId>ShrinkSDK.EventBus</PackageId>
<Version>2.1.0</Version> <Version>2.2.2</Version>
<Description>ShrinkSDK typed event bus runtime.</Description> <Description>ShrinkSDK typed event bus runtime.</Description>
<ShrinkCodeGenEnabled>false</ShrinkCodeGenEnabled> <ShrinkCodeGenEnabled>false</ShrinkCodeGenEnabled>
</PropertyGroup> </PropertyGroup>
@@ -13,7 +13,7 @@
<Compile Include="..\Runtime\**\*.cs" <Compile Include="..\Runtime\**\*.cs"
Exclude="..\Runtime\ShrinkMonoEventScope.cs" /> Exclude="..\Runtime\ShrinkMonoEventScope.cs" />
<PackageReference Include="UniTask" Version="2.5.10" /> <PackageReference Include="UniTask" Version="2.5.10" />
<PackageReference Include="ShrinkSDK.CodeGen" Version="0.1.0" PrivateAssets="compile;runtime;contentfiles;native" /> <PackageReference Include="ShrinkSDK.CodeGen" Version="0.2.0" PrivateAssets="compile;runtime;contentfiles;native" />
<PackageReference Include="ShrinkSDK.Runtime.Abstractions" Version="0.1.0" /> <PackageReference Include="ShrinkSDK.Runtime.Abstractions" Version="0.1.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+10
View File
@@ -182,3 +182,13 @@ ShrinkEventBusBenchmark.Run(
- Editor 诊断是可选观察层,不参与正式无诊断热路径。 - Editor 诊断是可选观察层,不参与正式无诊断热路径。
架构审计与来源说明见 `Docs/JustAnyProjectArchitectureAudit.md` 和根目录 `DESIGN.md`。 架构审计与来源说明见 `Docs/JustAnyProjectArchitectureAudit.md` 和根目录 `DESIGN.md`。
## 可复用能力
<!-- shrink:capabilities -->
Capability: 按类型发布事实和请求,支持同步、异步、优先级和可撤回订阅
Aliases: 事件 事件未触发 消息 订阅 通知 event bus
Limits: EventBus 不等于业务状态机或资源计量;并行派发须由业务明确选择
Extension: IShrinkEvent、ShrinkSubscribe、BusOptions 和拥有者 Attach/Dispose
Evidence: [EventBus](Runtime/EventBus.cs); [ShrinkSubscribeAttribute](Runtime/ShrinkEventAttributes.cs); [ShrinkGeneratedBinding](Runtime/ShrinkEventBindings.cs)
<!-- /shrink:capabilities -->
+8
View File
@@ -143,6 +143,14 @@ namespace ShrinkEventBus
return generated.AttachGenerated(Resolver, defaultBus ?? ShrinkBusKey.Game); return generated.AttachGenerated(Resolver, defaultBus ?? ShrinkBusKey.Game);
} }
public static IReadOnlyList<(Type EventType, System.Reflection.MethodInfo Handler)> CaptureBindings(IShrinkEventBus bus)
{
if (bus is ShrinkEventBusInstance instance) return instance.CaptureBindings();
throw new NotSupportedException("This event bus does not expose binding diagnostics.");
}
public static IReadOnlyList<IShrinkEventBus> CaptureNamedBuses() => new List<IShrinkEventBus>(Buses.Values);
internal static IReadOnlyList<(ShrinkBusKey Key, ShrinkBusOptions Options, int Subscribers)> Snapshot() internal static IReadOnlyList<(ShrinkBusKey Key, ShrinkBusOptions Options, int Subscribers)> Snapshot()
{ {
var snapshot = new List<(ShrinkBusKey, ShrinkBusOptions, int)>(Buses.Count); var snapshot = new List<(ShrinkBusKey, ShrinkBusOptions, int)>(Buses.Count);
+10
View File
@@ -14,6 +14,7 @@ namespace ShrinkEventBus
internal interface IShrinkEventChannel internal interface IShrinkEventChannel
{ {
int Count { get; } int Count { get; }
void CaptureBindings(List<(Type EventType, System.Reflection.MethodInfo Handler)> bindings);
bool RemoveSubscription(long subscriptionId); bool RemoveSubscription(long subscriptionId);
void Clear(); void Clear();
void DetachSlot(int slot); void DetachSlot(int slot);
@@ -87,6 +88,15 @@ namespace ShrinkEventBus
private Action<TEvent>? _syncDispatcher; private Action<TEvent>? _syncDispatcher;
public int Count => _snapshot.Length; public int Count => _snapshot.Length;
public void CaptureBindings(List<(Type EventType, System.Reflection.MethodInfo Handler)> bindings)
{
var snapshot = _snapshot;
foreach (var entry in snapshot)
{
var method = entry.SyncHandler?.Method ?? entry.AsyncHandler?.Method;
if (method != null) bindings.Add((typeof(TEvent), method));
}
}
public void Add(long subscriptionId, long registrationOrder, Action<TEvent> handler, public void Add(long subscriptionId, long registrationOrder, Action<TEvent> handler,
ShrinkSubscribeDescriptor descriptor) ShrinkSubscribeDescriptor descriptor)
+16 -4
View File
@@ -75,7 +75,7 @@ namespace ShrinkEventBus
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public virtual ShrinkPostResult Post<TEvent>(in TEvent eventData) where TEvent : IShrinkEvent public virtual ShrinkPostResult Post<TEvent>(in TEvent eventData) where TEvent : IShrinkEvent
{ {
if (eventData is null) if (!typeof(TEvent).IsValueType && eventData is null)
throw new ArgumentNullException(nameof(eventData)); throw new ArgumentNullException(nameof(eventData));
if (Volatile.Read(ref _disposed) != 0) if (Volatile.Read(ref _disposed) != 0)
return TraceWithoutDispatch(in eventData, return TraceWithoutDispatch(in eventData,
@@ -84,6 +84,11 @@ namespace ShrinkEventBus
if (_isInline || _scheduler.IsOnSchedulerThread) if (_isInline || _scheduler.IsOnSchedulerThread)
return Dispatch(in eventData); return Dispatch(in eventData);
return PostScheduled(in eventData);
}
private ShrinkPostResult PostScheduled<TEvent>(in TEvent eventData) where TEvent : IShrinkEvent
{
var queuedEvent = eventData; var queuedEvent = eventData;
if (_scheduler.TryPost(() => Dispatch(in queuedEvent))) if (_scheduler.TryPost(() => Dispatch(in queuedEvent)))
return new ShrinkPostResult(true, false, false); return new ShrinkPostResult(true, false, false);
@@ -94,7 +99,7 @@ namespace ShrinkEventBus
public virtual UniTask<ShrinkPostResult> PostAsync<TEvent>(TEvent eventData, public virtual UniTask<ShrinkPostResult> PostAsync<TEvent>(TEvent eventData,
CancellationToken cancellationToken = default) where TEvent : IShrinkEvent CancellationToken cancellationToken = default) where TEvent : IShrinkEvent
{ {
if (eventData is null) if (!typeof(TEvent).IsValueType && eventData is null)
throw new ArgumentNullException(nameof(eventData)); throw new ArgumentNullException(nameof(eventData));
if (Volatile.Read(ref _disposed) != 0) if (Volatile.Read(ref _disposed) != 0)
return UniTask.FromResult(TraceWithoutDispatch(in eventData, return UniTask.FromResult(TraceWithoutDispatch(in eventData,
@@ -168,6 +173,13 @@ namespace ShrinkEventBus
return new EventSubscription(this, subscriptionId); return new EventSubscription(this, subscriptionId);
} }
internal IReadOnlyList<(Type EventType, System.Reflection.MethodInfo Handler)> CaptureBindings()
{
var bindings = new List<(Type, System.Reflection.MethodInfo)>();
lock (_channelGate) foreach (var channel in _channels.Values) channel.CaptureBindings(bindings);
return bindings;
}
internal int SubscriberCount internal int SubscriberCount
{ {
get get
@@ -367,7 +379,7 @@ namespace ShrinkEventBus
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public override ShrinkPostResult Post<TEvent>(in TEvent eventData) public override ShrinkPostResult Post<TEvent>(in TEvent eventData)
{ {
if (eventData is null) if (!typeof(TEvent).IsValueType && eventData is null)
throw new ArgumentNullException(nameof(eventData)); throw new ArgumentNullException(nameof(eventData));
if (IsDisposed) if (IsDisposed)
return TraceWithoutDispatch(in eventData, return TraceWithoutDispatch(in eventData,
@@ -378,7 +390,7 @@ namespace ShrinkEventBus
public override UniTask<ShrinkPostResult> PostAsync<TEvent>(TEvent eventData, public override UniTask<ShrinkPostResult> PostAsync<TEvent>(TEvent eventData,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{ {
if (eventData is null) if (!typeof(TEvent).IsValueType && eventData is null)
throw new ArgumentNullException(nameof(eventData)); throw new ArgumentNullException(nameof(eventData));
if (IsDisposed) if (IsDisposed)
return UniTask.FromResult(TraceWithoutDispatch(in eventData, return UniTask.FromResult(TraceWithoutDispatch(in eventData,
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "com.cneicy.shrink-eventbus", "name": "com.cneicy.shrink-eventbus",
"version": "2.1.0", "version": "2.2.2",
"displayName": "ShrinkEventBus", "displayName": "ShrinkEventBus",
"description": "多 Bus、特性强类型注册、UniTask 调度与低分配发布的 Unity 事件总线。", "description": "多 Bus、特性强类型注册、UniTask 调度与低分配发布的 Unity 事件总线。",
"unity": "2022.3", "unity": "2022.3",
@@ -10,7 +10,7 @@
"dependencies": { "dependencies": {
"com.cysharp.unitask": "2.5.10", "com.cysharp.unitask": "2.5.10",
"com.unity.nuget.mono-cecil": "1.11.4", "com.unity.nuget.mono-cecil": "1.11.4",
"com.cneicy.shrink-shared-codegen": "0.1.1", "com.cneicy.shrink-shared-codegen": "0.2.1",
"com.cneicy.shrink-runtime-abstractions": "0.1.0" "com.cneicy.shrink-runtime-abstractions": "0.1.0"
}, },
"keywords": [ "keywords": [