-
Notifications
You must be signed in to change notification settings - Fork 180
feat: JMH test for event creator #21932
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 6 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
2c36a76
jmh test
lpetrovic05 650ff92
first benchmark working
lpetrovic05 c8c929c
done
lpetrovic05 b9e3292
cleanup
lpetrovic05 f07dcbf
s
lpetrovic05 a462b34
qg
lpetrovic05 31798f2
pr comments
lpetrovic05 f252aee
Merge branch 'main' into 21913-JMH-test-for-event-creator
lpetrovic05 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
197 changes: 197 additions & 0 deletions
197
...src/jmh/java/org/hiero/consensus/event/creator/impl/jmh/EventCreatorNetworkBenchmark.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,197 @@ | ||
| // SPDX-License-Identifier: Apache-2.0 | ||
| package org.hiero.consensus.event.creator.impl.jmh; | ||
|
|
||
| import com.hedera.hapi.node.state.roster.Roster; | ||
| import com.hedera.hapi.node.state.roster.RosterEntry; | ||
| import com.swirlds.base.time.Time; | ||
| import com.swirlds.common.context.PlatformContext; | ||
| import com.swirlds.common.test.fixtures.Randotron; | ||
| import com.swirlds.common.test.fixtures.WeightGenerators; | ||
| import com.swirlds.common.test.fixtures.platform.TestPlatformContextBuilder; | ||
| import com.swirlds.config.api.Configuration; | ||
| import com.swirlds.config.extensions.test.fixtures.TestConfigBuilder; | ||
| import com.swirlds.metrics.api.Metrics; | ||
| import com.swirlds.platform.event.orphan.DefaultOrphanBuffer; | ||
| import com.swirlds.platform.event.orphan.OrphanBuffer; | ||
| import com.swirlds.platform.gossip.NoOpIntakeEventCounter; | ||
| import com.swirlds.platform.test.fixtures.addressbook.RandomRosterBuilder; | ||
| import java.security.SecureRandom; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Random; | ||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.function.Function; | ||
| import org.hiero.consensus.event.creator.EventCreationConfig; | ||
| import org.hiero.consensus.event.creator.EventCreationConfig_; | ||
| import org.hiero.consensus.event.creator.impl.DefaultEventCreator; | ||
| import org.hiero.consensus.model.event.PlatformEvent; | ||
| import org.hiero.consensus.model.hashgraph.EventWindow; | ||
| import org.hiero.consensus.model.node.KeysAndCerts; | ||
| import org.hiero.consensus.model.node.NodeId; | ||
| import org.hiero.consensus.model.status.PlatformStatus; | ||
| import org.openjdk.jmh.annotations.Benchmark; | ||
| import org.openjdk.jmh.annotations.BenchmarkMode; | ||
| import org.openjdk.jmh.annotations.Fork; | ||
| import org.openjdk.jmh.annotations.Level; | ||
| import org.openjdk.jmh.annotations.Measurement; | ||
| import org.openjdk.jmh.annotations.Mode; | ||
| import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
| import org.openjdk.jmh.annotations.Param; | ||
| import org.openjdk.jmh.annotations.Scope; | ||
| import org.openjdk.jmh.annotations.Setup; | ||
| import org.openjdk.jmh.annotations.State; | ||
| import org.openjdk.jmh.annotations.Warmup; | ||
| import org.openjdk.jmh.infra.Blackhole; | ||
|
|
||
| /** | ||
| * Benchmark for measuring network-wide event creation throughput when multiple {@link DefaultEventCreator} instances | ||
| * create and share events. | ||
| * <p> | ||
| * This benchmark simulates a network of nodes where each node: | ||
| * <ul> | ||
| * <li>Creates events using its own DefaultEventCreator instance</li> | ||
| * <li>Shares created events with all other nodes in the network</li> | ||
| * <li>Receives and processes events from other nodes</li> | ||
| * </ul> | ||
| * <p> | ||
| * The benchmark measures the aggregate event creation rate across all nodes in the network, | ||
| * providing insight into network-wide throughput rather than single-node performance. | ||
| */ | ||
| @State(Scope.Benchmark) | ||
| @Fork(value = 1) | ||
| @Warmup(iterations = 1, time = 10) | ||
| @Measurement(iterations = 2, time = 10) | ||
| public class EventCreatorNetworkBenchmark { | ||
|
|
||
| /** The number of nodes in the simulated network. */ | ||
| @Param({"4", "8"}) | ||
| public int numNodes; | ||
|
|
||
| /** Random seed for reproducibility. */ | ||
| @Param({"0"}) | ||
| public long seed; | ||
|
|
||
| /** The event creators for each node in the network. */ | ||
| private List<DefaultEventCreator> eventCreators; | ||
|
|
||
| /** The roster defining the network. */ | ||
| private Roster roster; | ||
|
|
||
| /** Keys and certificates for each node in the network. */ | ||
| private Function<NodeId, KeysAndCerts> nodeKeysAndCerts; | ||
|
|
||
| /** Total number of events created in the current iteration. */ | ||
| private int eventsCreatedInIteration; | ||
|
|
||
| /** Current event window for the network. */ | ||
| private EventWindow eventWindow; | ||
|
|
||
| /** The number of events after which the event window should be updated */ | ||
| private long eventWindowUpdateInterval; | ||
|
|
||
| /** Orphan buffer, required to set the nGen value needed by the event creator */ | ||
| private OrphanBuffer orphanBuffer; | ||
|
|
||
| @Setup(Level.Trial) | ||
| public void setupTrial() { | ||
| // Build a roster with real keys | ||
| final RandomRosterBuilder rosterBuilder = RandomRosterBuilder.create(Randotron.create(seed)) | ||
| .withSize(numNodes) | ||
| .withWeightGenerator(WeightGenerators.BALANCED) | ||
| .withRealKeysEnabled(true); | ||
| roster = rosterBuilder.build(); | ||
| nodeKeysAndCerts = rosterBuilder::getPrivateKeys; | ||
| eventWindowUpdateInterval = Math.round(numNodes * Math.log(numNodes)); | ||
| } | ||
|
|
||
| @Setup(Level.Iteration) | ||
| public void setupIteration() { | ||
| eventCreators = new ArrayList<>(numNodes); | ||
| eventWindow = EventWindow.getGenesisEventWindow(); | ||
| final Configuration configuration = new TestConfigBuilder() | ||
| .withConfigDataType(EventCreationConfig.class) | ||
| .withValue(EventCreationConfig_.MAX_CREATION_RATE, 0) | ||
| .getOrCreateConfig(); | ||
| final PlatformContext platformContext = TestPlatformContextBuilder.create() | ||
| .withConfiguration(configuration) | ||
| .build(); | ||
| final Metrics metrics = platformContext.getMetrics(); | ||
| final Time time = platformContext.getTime(); | ||
|
|
||
| // Create an event creator for each node | ||
| for (final RosterEntry entry : roster.rosterEntries()) { | ||
| final NodeId nodeId = NodeId.of(entry.nodeId()); | ||
| final KeysAndCerts keysAndCerts = nodeKeysAndCerts.apply(nodeId); | ||
| final SecureRandom nodeRandom = new SecureRandom(); | ||
| nodeRandom.setSeed(new Random(seed).nextLong()); | ||
lpetrovic05 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| final DefaultEventCreator eventCreator = new DefaultEventCreator(); | ||
| eventCreator.initialize( | ||
| configuration, metrics, time, nodeRandom, keysAndCerts, roster, nodeId, List::of, () -> false); | ||
|
|
||
| // Set platform status to ACTIVE so events can be created | ||
| eventCreator.updatePlatformStatus(PlatformStatus.ACTIVE); | ||
| eventCreator.setEventWindow(eventWindow); | ||
|
|
||
| eventCreators.add(eventCreator); | ||
| } | ||
| orphanBuffer = new DefaultOrphanBuffer(metrics, new NoOpIntakeEventCounter()); | ||
|
|
||
| eventsCreatedInIteration = 0; | ||
| } | ||
|
|
||
| /** | ||
| * Benchmark that measures network-wide event creation throughput. | ||
| * <p> | ||
| * In each iteration: | ||
| * <ol> | ||
| * <li>Each node attempts to create an event, until one is created</li> | ||
| * <li>Successfully created events are shared with all other nodes</li> | ||
| * </ol> | ||
| * <p> | ||
| * | ||
| * @param bh JMH blackhole to prevent dead code elimination | ||
| */ | ||
| @Benchmark | ||
| @BenchmarkMode(Mode.Throughput) | ||
| @OutputTimeUnit(TimeUnit.SECONDS) | ||
| public void networkEventCreation(final Blackhole bh) { | ||
| PlatformEvent newEvent = null; | ||
| for (final DefaultEventCreator creator : eventCreators) { | ||
| final PlatformEvent event = creator.maybeCreateEvent(); | ||
| if (event != null) { | ||
| newEvent = event; | ||
| bh.consume(event); | ||
| break; | ||
| } | ||
| } | ||
| if (newEvent == null) { | ||
| throw new RuntimeException("At least one creator should always be able to create an event"); | ||
| } | ||
| final List<PlatformEvent> unorphanedEvents = orphanBuffer.handleEvent(newEvent); | ||
| if (unorphanedEvents.size() != 1) { | ||
| throw new RuntimeException("There should be no orphaned events in this benchmark"); | ||
| } | ||
|
|
||
| // Share newly created events with all nodes (simulating gossip) | ||
| for (final DefaultEventCreator creator : eventCreators) { | ||
| creator.registerEvent(newEvent); | ||
| } | ||
|
|
||
| eventsCreatedInIteration++; | ||
|
|
||
| // Periodically update event window to simulate consensus progress | ||
| if (eventsCreatedInIteration % eventWindowUpdateInterval == 0) { | ||
| eventWindow = new EventWindow( | ||
| eventWindow.latestConsensusRound() + 1, | ||
| eventWindow.newEventBirthRound() + 1, | ||
| Math.max(1, eventWindow.latestConsensusRound() - 25), | ||
| Math.max(1, eventWindow.latestConsensusRound() - 25)); | ||
|
|
||
| for (final DefaultEventCreator creator : eventCreators) { | ||
| creator.setEventWindow(eventWindow); | ||
| } | ||
| eventsCreatedInIteration = 0; | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.