Splitting callback overhead: off vs global-enable vs setup-then-remove vs active no-op, all on one executable
To find what a simulator value-change subscription actually costs, build ONE executable and switch arms at run time. Four arms: (1) capture off; (2) the global capture enable on with no per-signal subscriptions; (3) subscriptions registered and then removed before the design runs; (4) subscriptions active with a no-op callback body. (2) minus (1) is the global enable cost; (3) minus (1) is the one-time setup cost plus any lasting state effects; (4) minus (3) is the ongoing cost of being subscribed and called. In one measurement, arms 1–3 were within 0.02 CPU-s and arm 4 added about 6%. That ruled out optimising the callback body (e.g. caching the time query), because the no-op arm makes no time queries or copies at all. Keep notification and selected-hit counts identical across arms as a check, use separate builds only when unavoidable, and give each build its own baseline. Treat n=1 deltas near the timer resolution as scoping only.