From 4bc15ddb6c4244a0f292be22af6b3e45403cdd8d Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Mon, 15 Dec 2025 15:45:02 +0100 Subject: [PATCH 1/3] Only check isomorpism of types in debug compiles takes >10s compile time, every time .. --- beacon_chain/spec/datatypes/base.nim | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index f2cd6d3faf..60ee63986b 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -865,7 +865,7 @@ static: doAssert supportsCopyMem(Eth2Digest) doAssert ATTESTATION_SUBNET_COUNT <= high(distinctBase SubnetId) -func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] = +func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] {.compileTime.} = for name, value in x.fieldPairs: when value is tuple|object: result.add getSizeofSig(value, n + 1) @@ -884,19 +884,22 @@ func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] = template isomorphicCast*[T](x: auto): T = # Each of these pairs of types has ABI-compatible memory representations. type U = typeof(x) + static: doAssert (T is ref) == (U is ref) when T is ref: - type - TT = typeof default(typeof T)[] - UU = typeof default(typeof U)[] - static: - doAssert sizeof(TT) == sizeof(UU) - doAssert getSizeofSig(TT()) == getSizeofSig(UU()) + when defined(debug): + type + TT = typeof default(typeof T)[] + UU = typeof default(typeof U)[] + static: + doAssert sizeof(TT) == sizeof(UU) + doAssert getSizeofSig(TT()) == getSizeofSig(UU()) cast[T](x) else: - static: - doAssert getSizeofSig(T()) == getSizeofSig(U()) - doAssert sizeof(T) == sizeof(U) + when defined(debug): # 10s+ compile time due to `default(T)` and `replace`! + static: + doAssert getSizeofSig(T()) == getSizeofSig(U()) + doAssert sizeof(T) == sizeof(U) cast[ptr T](unsafeAddr x)[] func prune*(cache: var StateCache, epoch: Epoch) = From e82bc931a9f71d293e9459b81da2f415428b0cf2 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 18 Dec 2025 07:39:15 +0100 Subject: [PATCH 2/3] more efficient field type pattern `default(T)` must create a T in the compiler VM which is slow --- beacon_chain/libnimbus_lc/libnimbus_lc.nim | 3 ++- beacon_chain/spec/datatypes/base.nim | 26 ++++++++++------------ beacon_chain/spec/state_transition.nim | 10 +++++---- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/beacon_chain/libnimbus_lc/libnimbus_lc.nim b/beacon_chain/libnimbus_lc/libnimbus_lc.nim index 1018507d64..6da1790071 100644 --- a/beacon_chain/libnimbus_lc/libnimbus_lc.nim +++ b/beacon_chain/libnimbus_lc/libnimbus_lc.nim @@ -9,6 +9,7 @@ import std/[json, sequtils, times], + stew/objects, eth/common/eth_types_rlp, eth/common/keys, eth/p2p/discoveryv5/random2, @@ -957,7 +958,7 @@ proc ETHLightClientHeaderCopyExecutionHash( root.toUnmanagedPtr() type ExecutionPayloadHeader = - typeof(default(lcDataFork.LightClientHeader).execution) + typeof(declval(lcDataFork.LightClientHeader).execution) func ETHLightClientHeaderGetExecution( header: ptr lcDataFork.LightClientHeader diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 60ee63986b..8b09cbdfd1 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -865,10 +865,10 @@ static: doAssert supportsCopyMem(Eth2Digest) doAssert ATTESTATION_SUBNET_COUNT <= high(distinctBase SubnetId) -func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] {.compileTime.} = - for name, value in x.fieldPairs: +func getSizeofSig(X: type, n: int = 0): seq[(string, int, int)] {.compileTime.} = + for name, value in default(ptr X)[].fieldPairs: when value is tuple|object: - result.add getSizeofSig(value, n + 1) + result.add getSizeofSig(typeof(value), n + 1) # TrustedSig and ValidatorSig differ in that they have otherwise identical # fields where one is "blob" and the other is "data". They're structurally # isomorphic, regardless. Grandfather that exception in, but in general it @@ -887,19 +887,17 @@ template isomorphicCast*[T](x: auto): T = static: doAssert (T is ref) == (U is ref) when T is ref: - when defined(debug): - type - TT = typeof default(typeof T)[] - UU = typeof default(typeof U)[] - static: - doAssert sizeof(TT) == sizeof(UU) - doAssert getSizeofSig(TT()) == getSizeofSig(UU()) + type + TT = pointerBase(T) + UU = pointerBase(U) + static: + doAssert sizeof(TT) == sizeof(UU) + doAssert getSizeofSig(T) == getSizeofSig(U) cast[T](x) else: - when defined(debug): # 10s+ compile time due to `default(T)` and `replace`! - static: - doAssert getSizeofSig(T()) == getSizeofSig(U()) - doAssert sizeof(T) == sizeof(U) + static: + doAssert getSizeofSig(T) == getSizeofSig(U) + doAssert sizeof(T) == sizeof(U) cast[ptr T](unsafeAddr x)[] func prune*(cache: var StateCache, epoch: Epoch) = diff --git a/beacon_chain/spec/state_transition.nim b/beacon_chain/spec/state_transition.nim index d53a0f6519..45f373b765 100644 --- a/beacon_chain/spec/state_transition.nim +++ b/beacon_chain/spec/state_transition.nim @@ -43,10 +43,12 @@ import chronicles, results, + stew/objects, ../extras, - "."/[ - beaconstate, eth2_merkleization, forks, helpers, signatures, - state_transition_block, state_transition_epoch, validator] + ./[ + beaconstate, eth2_merkleization, forks, helpers, signatures, state_transition_block, + state_transition_epoch, validator, + ] export results, extras, state_transition_block @@ -377,7 +379,7 @@ proc makeBeaconBlockWithRewards*( ## the block is to be created. type MaybeBlindedBeaconBlock = consensusFork.BeaconBlock(type(execution_payload)) - MaybeBlindedBlockBody = typeof(default(MaybeBlindedBeaconBlock).body) + MaybeBlindedBlockBody = typeof(declval(MaybeBlindedBeaconBlock).body) # https://github.com/ethereum/consensus-specs/blob/v1.5.0-beta.2/specs/phase0/validator.md#preparing-for-a-beaconblock var blck = MaybeBlindedBeaconBlock( From afd89c4238b510a599a2a8593964dc5b9dcb31d4 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 18 Dec 2025 08:01:06 +0100 Subject: [PATCH 3/3] fix --- beacon_chain/spec/datatypes/base.nim | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/beacon_chain/spec/datatypes/base.nim b/beacon_chain/spec/datatypes/base.nim index 8b09cbdfd1..ea5b45a8f2 100644 --- a/beacon_chain/spec/datatypes/base.nim +++ b/beacon_chain/spec/datatypes/base.nim @@ -865,10 +865,10 @@ static: doAssert supportsCopyMem(Eth2Digest) doAssert ATTESTATION_SUBNET_COUNT <= high(distinctBase SubnetId) -func getSizeofSig(X: type, n: int = 0): seq[(string, int, int)] {.compileTime.} = - for name, value in default(ptr X)[].fieldPairs: +func getSizeofSig(x: auto, n: int = 0): seq[(string, int, int)] = + for name, value in x.fieldPairs: when value is tuple|object: - result.add getSizeofSig(typeof(value), n + 1) + result.add getSizeofSig(value, n + 1) # TrustedSig and ValidatorSig differ in that they have otherwise identical # fields where one is "blob" and the other is "data". They're structurally # isomorphic, regardless. Grandfather that exception in, but in general it @@ -887,17 +887,19 @@ template isomorphicCast*[T](x: auto): T = static: doAssert (T is ref) == (U is ref) when T is ref: - type - TT = pointerBase(T) - UU = pointerBase(U) - static: - doAssert sizeof(TT) == sizeof(UU) - doAssert getSizeofSig(T) == getSizeofSig(U) + when defined(debug): + type + TT = pointerBase(T) + UU = pointerBase(U) + static: + doAssert sizeof(TT) == sizeof(UU) + doAssert getSizeofSig(TT()) == getSizeofSig(UU()) cast[T](x) else: - static: - doAssert getSizeofSig(T) == getSizeofSig(U) - doAssert sizeof(T) == sizeof(U) + when defined(debug): # 10s+ compile time due to `default(T)` and `replace`! + static: + doAssert sizeof(T) == sizeof(U) + doAssert getSizeofSig(T()) == getSizeofSig(U()) cast[ptr T](unsafeAddr x)[] func prune*(cache: var StateCache, epoch: Epoch) =