-
- {title}
-
-
- {description}
-
+
+ {/* Title section */}
+
+ {title}
+
+ {description && (
+ <>
+ {/* Vertical divider in the middle - only when description exists */}
+
+
+ {/* Description section */}
+
+ >
+ )}
{children}
)
diff --git a/frontend/components/ui/switch.tsx b/frontend/components/ui/switch.tsx
index 72402c9..197edd5 100644
--- a/frontend/components/ui/switch.tsx
+++ b/frontend/components/ui/switch.tsx
@@ -13,7 +13,8 @@ function Switch({
diff --git a/frontend/hooks/use-block-execution-tracker.ts b/frontend/hooks/use-block-execution-tracker.ts
index cb39d49..d82b8ee 100644
--- a/frontend/hooks/use-block-execution-tracker.ts
+++ b/frontend/hooks/use-block-execution-tracker.ts
@@ -1,11 +1,19 @@
import { useCallback, useMemo, useState } from 'react'
-import { fromNsToMsPrecise } from '@/lib/block-metrics'
+import {
+ fromNsToMsPrecise,
+ getBlockWallTimeMs,
+ getTotalTransactionTimeMs,
+} from '@/lib/block-metrics'
import type { Block } from '@/types/block'
import type { SerializableEventData } from '@/types/events'
import { useEvents } from './use-events'
const MAX_BLOCKS = 5000
+// Highlight when total tx execution time exceeds block execution time.
+// Keep this as a single constant so UI/copy can stay consistent.
+export const PARALLEL_EXECUTION_RATIO_THRESHOLD = 1
+
/**
* Hook to track block execution events and derive timing metrics.
*/
@@ -257,22 +265,26 @@ export function useBlockExecutionTracker() {
)
}, [finalizedBlocks])
- const normalizedBlockExecutionTime = useMemo(() => {
+ const normalizedTimeScaleMs = useMemo(() => {
if (finalizedBlocks.length === 0) return 1
- // Get all execution times in milliseconds
- const executionTimes = finalizedBlocks
- .map((block) => fromNsToMsPrecise(block.executionTime ?? BigInt(0)))
+ // Normalize against the larger of:
+ // - block wall-time (BlockEnd - BlockStart)
+ // - ΣTx execution time (sum of TxnHeaderEnd - TxnHeaderStart)
+ // This lets the visualization show when ΣTx > block time (parallel overlap).
+ const maxTimes = finalizedBlocks
+ .map((block) =>
+ Math.max(getBlockWallTimeMs(block), getTotalTransactionTimeMs(block)),
+ )
.filter((time) => time > 0)
.sort((a, b) => a - b)
- if (executionTimes.length === 0) return 1
+ if (maxTimes.length === 0) return 1
// Use 95th percentile to be resistant to spikes
- const percentileIndex = Math.floor(executionTimes.length * 0.95)
+ const percentileIndex = Math.floor(maxTimes.length * 0.95)
const percentile95 =
- executionTimes[percentileIndex] ||
- executionTimes[executionTimes.length - 1]
+ maxTimes[percentileIndex] || maxTimes[maxTimes.length - 1]
// Add 10% buffer to prevent clipping of high values near the percentile
return percentile95 * 1.1
@@ -282,6 +294,6 @@ export function useBlockExecutionTracker() {
blocks,
finalizedBlocks,
maxBlockExecutionTime,
- normalizedBlockExecutionTime,
+ normalizedTimeScaleMs,
}
}
diff --git a/frontend/lib/block-metrics.ts b/frontend/lib/block-metrics.ts
index 87e1671..c54c16f 100644
--- a/frontend/lib/block-metrics.ts
+++ b/frontend/lib/block-metrics.ts
@@ -12,58 +12,76 @@ export function fromNsToMsPrecise(ns: bigint): number {
return msPart + nsPart / 1_000_000
}
+function sumTransactionTimeNs(block: Block): bigint {
+ return (block.transactions ?? []).reduce(
+ (sum, tx) => sum + BigInt(tx.transactionTime ?? 0),
+ BigInt(0),
+ )
+}
+
+export function getBlockWallTimeMs(block: Block): number {
+ return fromNsToMsPrecise(block.executionTime ?? BigInt(0))
+}
+
+export function getTotalTransactionTimeMs(block: Block): number {
+ return fromNsToMsPrecise(sumTransactionTimeNs(block))
+}
+
+export function getParallelizationRatio(
+ blockMs: number,
+ totalTxMs: number,
+): number {
+ if (!(blockMs > 0)) return 0
+ const ratio = totalTxMs / blockMs
+ return Number.isFinite(ratio) ? ratio : 0
+}
+
+export function getTimeSavedMs(blockMs: number, totalTxMs: number): number {
+ return Math.max(totalTxMs - blockMs, 0)
+}
+
/**
* Calculate bar metrics for a block visualization
* @param block - The block to calculate metrics for
- * @param maxBlockExecutionTime - The maximum block execution time in the dataset for normalization
- * @returns Object containing bar height percentage and fill percentage
+ * @param normalizedTimeScaleMs - Shared normalization scale for both bars (ms)
+ * @returns Object containing bar height percentages and tooltip metrics
*/
export function calculateBarMetrics(
block: Block,
- maxBlockExecutionTime: number,
+ normalizedTimeScaleMs: number,
+ parallelExecutionRatioThreshold: number,
) {
- const blockExecutionTime = block.executionTime ?? BigInt(0)
-
- // Calculate total transaction execution time with high precision
- const totalTransactionTimeNs = (block.transactions ?? []).reduce(
- (sum, tx) => sum + BigInt(tx.transactionTime ?? 0),
- BigInt(0),
+ const blockMs = getBlockWallTimeMs(block)
+ const totalTransactionTime = getTotalTransactionTimeMs(block)
+ const parallelizationRatio = getParallelizationRatio(
+ blockMs,
+ totalTransactionTime,
)
- const totalTransactionTime = fromNsToMsPrecise(totalTransactionTimeNs)
+ const isParallelExecution =
+ parallelizationRatio > parallelExecutionRatioThreshold
+
+ const timeSavedMs = getTimeSavedMs(blockMs, totalTransactionTime)
- // Normalize bar height based on block time (container represents block execution time)
- // If no execution time, show minimal height for blocks that exist
- const barHeightPercentage =
- Number(blockExecutionTime) > 0
- ? (fromNsToMsPrecise(blockExecutionTime) / maxBlockExecutionTime) * 100
- : 20 // Show something for blocks without execution time yet
+ const scaleMs = normalizedTimeScaleMs > 0 ? normalizedTimeScaleMs : 1
- // Calculate fill percentage (transaction time relative to block time)
- // If transactions run in parallel, total transaction time can be > block time
- // But the fill can't exceed 100% of the container
- const fillPercentage =
- Number(blockExecutionTime) > 0
- ? Math.min(
- (totalTransactionTime / fromNsToMsPrecise(blockExecutionTime)) * 100,
- 100,
- )
- : totalTransactionTime > 0
- ? 50
- : 0 // Show some fill if we have transactions
+ // Normalize both bars using the same scale so that ΣTx can exceed block time.
+ // If no execution time yet, show a minimal visible placeholder.
+ const blockHeightPct = blockMs > 0 ? (blockMs / scaleMs) * 100 : 20
+ const txHeightPct =
+ totalTransactionTime > 0 ? (totalTransactionTime / scaleMs) * 100 : 0
- // Calculate efficiency metrics
- const parallelizationRatio =
- Number(blockExecutionTime) > 0
- ? totalTransactionTime / fromNsToMsPrecise(blockExecutionTime)
- : 0
- const isHighlyParallel =
- parallelizationRatio > 1 && Number.isFinite(parallelizationRatio)
+ // Calculate parallel efficiency as percentage
+ const parallelEfficiencyPct =
+ totalTransactionTime > 0 ? (timeSavedMs / totalTransactionTime) * 100 : 0
return {
- barHeightPercentage: Math.max(barHeightPercentage * 0.8, 15), // Ensure minimum height
- fillPercentage,
+ blockHeightPct: Math.max(Math.min(blockHeightPct, 100) * 0.9, 15), // Ensure minimum height + a little headroom
+ txHeightPct: Math.min(txHeightPct, 100) * 0.9,
+ blockMs,
totalTransactionTime,
parallelizationRatio,
- isHighlyParallel,
+ isParallelExecution,
+ timeSavedMs,
+ parallelEfficiencyPct,
}
}