refactor: optimize event listener count checks (#8293)

## Summary

This PR optimizes event listener count checks for
`routes.events.EventType` emissions, following the principle that
**listener count checks should only be added where there's expensive
preprocessing work before emit**, since `emit()` is already a no-op when
no listeners exist.

## Changes Made

###  Added listener count checks (where expensive preprocessing
occurs):

**`packages/beacon-node/src/network/processor/gossipHandlers.ts`:**
- **`dataColumnSidecar`** emission: Added check to avoid
`kzgCommitments.map(toHex)` array mapping when no listeners

**`packages/beacon-node/src/chain/prepareNextSlot.ts`:**
- **`payloadAttributes`** emission: Added check to avoid `await
getPayloadAttributesForSSE()` async function call when no listeners

###  Removed unnecessary listener count checks (where no expensive
preprocessing occurs):

**`packages/beacon-node/src/network/processor/gossipHandlers.ts`:**
- **`blockGossip`** emission: Removed check since it only uses existing
variables `{slot, block: blockRootHex}`

**`packages/beacon-node/src/api/impl/beacon/blocks/index.ts`:**
- **`blockGossip`** emission: Removed check since it's a simple emission
with existing variables

###  Kept existing correct checks (for expensive operations):

- **`blobSidecar`** emissions: Keep checks for `toHex()` conversions and
`kzgCommitmentToVersionedHash()`
- **All loop-based emissions** in `importBlock.ts`: Keep checks for
`for` loop iterations
- **`block`** emission: Keep check for `isOptimisticBlock()` computation

## Performance Benefits

1. **Reduced CPU usage**: Expensive operations like async function calls
and array mapping only occur when needed
2. **Better resource utilization**: Memory allocations and computations
are avoided when events won't be consumed
3. **Cleaner code**: Removed redundant checks where the emit operation
itself is lightweight

## Key Principle Applied

**Only add listener count checks where there's expensive preprocessing
work before emission:**
-  Function calls, array operations, crypto operations
-  Simple emissions using existing variables

Since `emit()` is already a no-op when no listeners exist, explicit
checks are only beneficial when avoiding preprocessing overhead.

## Testing

-  Type checks pass
-  Build succeeds  
-  Linting passes

Resolves #7996

🤖 Generated with [Claude Code](https://claude.ai/code)

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Nico Flaig
2025-08-29 12:40:35 +01:00
committed by GitHub
parent 74da098ba7
commit 26ea9fa05b
3 changed files with 14 additions and 13 deletions

View File

@@ -308,9 +308,7 @@ export function getBeaconBlockApi({
}
}
if (chain.emitter.listenerCount(routes.events.EventType.blockGossip)) {
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRoot});
}
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRoot});
if (blockForImport.type === BlockInputType.availableData) {
if (isForkPostFulu(blockForImport.blockData.fork)) {

View File

@@ -191,7 +191,10 @@ export class PrepareNextSlotScheduler {
this.computeStateHashTreeRoot(updatedPrepareState, isEpochTransition);
// If emitPayloadAttributes is true emit a SSE payloadAttributes event
if (this.chain.opts.emitPayloadAttributes === true) {
if (
this.chain.opts.emitPayloadAttributes === true &&
this.chain.emitter.listenerCount(routes.events.EventType.payloadAttributes)
) {
const data = await getPayloadAttributesForSSE(fork as ForkPostBellatrix, this.chain, {
prepareState: updatedPrepareState,
prepareSlot,

View File

@@ -176,9 +176,7 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
logger.debug("Validated gossip block", {...logCtx, recvToValidation, validationTime});
if (chain.emitter.listenerCount(routes.events.EventType.blockGossip)) {
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRootHex});
}
chain.emitter.emit(routes.events.EventType.blockGossip, {slot, block: blockRootHex});
return blockInput;
} catch (e) {
@@ -311,12 +309,14 @@ function getSequentialHandlers(modules: ValidatorFnsModules, options: GossipHand
metrics?.gossipBlob.recvToValidation.observe(recvToValidation);
metrics?.gossipBlob.validationTime.observe(validationTime);
chain.emitter.emit(routes.events.EventType.dataColumnSidecar, {
blockRoot: blockRootHex,
slot,
index: dataColumnSidecar.index,
kzgCommitments: dataColumnSidecar.kzgCommitments.map(toHex),
});
if (chain.emitter.listenerCount(routes.events.EventType.dataColumnSidecar)) {
chain.emitter.emit(routes.events.EventType.dataColumnSidecar, {
blockRoot: blockRootHex,
slot,
index: dataColumnSidecar.index,
kzgCommitments: dataColumnSidecar.kzgCommitments.map(toHex),
});
}
logger.debug("Received gossip dataColumn", {
slot: slot,