Numbers on this page were captured, not estimated. A unit test (Test_DispatchStack, tagged TSTK, in
GMPTests.cpp) calls FPlatformStackWalk::CaptureStackBackTrace from inside the listener body and
counts the GMP frames sitting between the send call and the callback.
The same listener is hit two ways:
SendObjectMessage(MSGKEY(...), ...), the FName plus TMap lookup pathMSGKEY_SLOT(...) plus SendObjectMessageDirect(...), the baked-key pathReproduce:
dotnet Engine/Binaries/DotNET/UnrealBuildTool/UnrealBuildTool.dll \
<YourEditor> Win64 <DebugGame|Development> -Project=<Your.uproject> -Module=GMP -WaitMutex
Engine/Binaries/Win64/UnrealEditor[-Win64-DebugGame]-Cmd.exe \
<Your.uproject> -run=GMPUnitTest -unattended -nopause -nosplash -nullrhi -NoShaderCompile
| Build | by-name (FName + lookup) | by-store (baked key) |
|---|---|---|
| Unoptimized (DebugGame) | 9 | 7 |
| Optimized (Development) | 4 | 3 |
Frame counts exclude the listener callback itself.
Both rows are modular builds. GMP_WITH_INLINE_FIRE only takes effect when GMP_WITH_STATIC_STORE
is on, and that requires GMP_STATIC_STORE_MONOLITHIC (IS_MONOLITHIC). What it removes is the first two
of the three optimized frames: the cross-module GMP_API wrapper disappears, and the dispatch loop inlines
into the caller.
Going from 9 to 3 is two separate effects:
SendObjectMessageWrapperEx → NotifyMessageImpl →
FireMsgBodyAdapt) into a single NotifyMessageDirectRawFlexBackendThunk,
TGMPFunction::operator() and the unpack thunkWhat survives optimization is the work that actually has to happen: find the store, walk the matched listeners, call them.
by-name — 9 frames
GMP::FMessageHub::SendObjectMessageWrapperEx<0,int>()GMP::FMessageHub::NotifyMessageImpl()GMP::FireMsgBodyAdapt()GMP::GMPFireWithSigSourceDirectRaw()GMP::FSignalUtils::FireWithSigSourceCore<0, ...FireWithSigSourceRaw<0>...<lambda_1> >()GMP::FSignalUtils::FireWithSigSourceRaw<0>::<lambda_1>::operator()()GMP::FlexSig::TFlexBackendThunk<GMP::TGMPFunction<void __cdecl(FGMPTypedAddr const*, GMP::FGMPExtra const*)>, FGMPTypedAddr const*, GMP::FGMPExtra const*>::FlexThunk()GMP::TGMPFunction<void __cdecl(FGMPTypedAddr const*, GMP::FGMPExtra const*)>::operator()()GMP::Hub::RawUnpackThunk<std::tuple<int>, ...Test_DispatchStack...<lambda_1> >()by-store (baked key) — 7 frames
GMP::FMessageHub::NotifyMessageDirectRaw()GMP::GMPFireWithSigSourceDirectRaw()GMP::FSignalUtils::FireWithSigSourceCore<0, ...>()GMP::FSignalUtils::FireWithSigSourceRaw<0>::<lambda_1>::operator()()GMP::FlexSig::TFlexBackendThunk<...>::FlexThunk()GMP::TGMPFunction<...>::operator()()GMP::Hub::RawUnpackThunk<...>()by-name — 4 frames
GMP::FMessageHub::SendObjectMessageWrapperEx<0,int>()GMP::FMessageHub::NotifyMessageImpl()GMP::GMPFireWithSigSourceDirectRaw()GMP::FSignalUtils::FireWithSigSourceCore<0, ...>()by-store (baked key) — 3 frames
GMP::FMessageHub::NotifyMessageDirectRaw()GMP::GMPFireWithSigSourceDirectRaw() — GMP_API, the export the optimizer cannot inline throughGMP::FSignalUtils::FireWithSigSourceCore<0, ...>() — the matching loopSymbols are as the platform stack walker printed them; template arguments are elided above where they only repeat the enclosing signature.