Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions platform-sdk/consensus-event-creator-impl/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,23 @@
plugins {
id("org.hiero.gradle.module.library")
id("org.hiero.gradle.feature.publish-artifactregistry")
id("org.hiero.gradle.feature.benchmark")
}

mainModuleInfo { annotationProcessor("com.swirlds.config.processor") }

jmhModuleInfo {
requires("com.swirlds.common")
requires("com.swirlds.common.test.fixtures")
requires("com.swirlds.config.extensions.test.fixtures")
requires("com.swirlds.platform.core")
requires("com.swirlds.platform.core.test.fixtures")
requires("org.hiero.consensus.event.creator")
requires("org.hiero.consensus.event.creator.impl")
requires("com.hedera.node.hapi")
requires("jmh.core")
}

description = "Default Consensus Event Creator Implementation"

testModuleInfo {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
// 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.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 event creation throughput of {@link DefaultEventCreator} instances. This benchmark runs
* multiple event creators in a single thread. Although this is not a completely accurate benchmark of a single event
* creator, it should be a good approximation of throughput. The reason for using multiple event creators is that it
* is not trivial to use just one, since it has to build on top of events created by other nodes.
*/
@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(nodeId.id());

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 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;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ public static Map<NodeId, SimulatedNode> buildSimulatedNodes(
final ChildlessEventTracker childlessEventTracker = new ChildlessEventTracker();
final TipsetWeightCalculator tipsetWeightCalculator = new TipsetWeightCalculator(
configuration, time, roster, NodeId.of(address.nodeId()), tipsetTracker, childlessEventTracker);
final OrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, mock(IntakeEventCounter.class));
final OrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, mock(IntakeEventCounter.class));

eventCreators.put(
selfId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,10 +381,7 @@
@NonNull
public OrphanBuffer buildOrphanBuffer() {
if (orphanBuffer == null) {
orphanBuffer = new DefaultOrphanBuffer(
blocks.platformContext().getConfiguration(),
blocks.platformContext().getMetrics(),
blocks.intakeEventCounter());
orphanBuffer = new DefaultOrphanBuffer(blocks.platformContext().getMetrics(), blocks.intakeEventCounter());

Check warning on line 384 in platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java

View check run for this annotation

Codecov / codecov/patch

platform-sdk/swirlds-platform-core/src/main/java/com/swirlds/platform/builder/PlatformComponentBuilder.java#L384

Added line #L384 was not covered by tests
}
return orphanBuffer;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import static org.hiero.consensus.model.event.NonDeterministicGeneration.assignNGen;

import com.swirlds.common.metrics.FunctionGauge;
import com.swirlds.config.api.Configuration;
import com.swirlds.metrics.api.Metrics;
import com.swirlds.platform.gossip.IntakeEventCounter;
import edu.umd.cs.findbugs.annotations.NonNull;
Expand Down Expand Up @@ -67,14 +66,10 @@ public class DefaultOrphanBuffer implements OrphanBuffer {
/**
* Constructor
*
* @param configuration the platform configuration
* @param metrics the metrics instance to use
* @param intakeEventCounter keeps track of the number of events in the intake pipeline from each peer
*/
public DefaultOrphanBuffer(
@NonNull final Configuration configuration,
@NonNull final Metrics metrics,
@NonNull final IntakeEventCounter intakeEventCounter) {
public DefaultOrphanBuffer(@NonNull final Metrics metrics, @NonNull final IntakeEventCounter intakeEventCounter) {

this.intakeEventCounter = Objects.requireNonNull(intakeEventCounter);
this.currentOrphanCount = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void simpleGraphTest() {
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, Mockito.mock(IntakeEventCounter.class));
new DefaultOrphanBuffer(metrics, Mockito.mock(IntakeEventCounter.class));
// Create a simple graph
events = SimpleGraphs.graph8e4n(randotron).stream()
.peek(orphanBuffer::handleEvent)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ void standardOperation() {
final Configuration configuration =
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(configuration, metrics, intakeEventCounter);
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, intakeEventCounter);

long latestConsensusRound = ConsensusConstants.ROUND_FIRST;

Expand Down Expand Up @@ -253,7 +253,7 @@ void topologicalOrderByNGen() {
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final IntakeEventCounter intakeEventCounter = mock(IntakeEventCounter.class);
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(configuration, metrics, intakeEventCounter);
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, intakeEventCounter);

final List<PlatformEvent> emittedEvents = new ArrayList<>();
for (final PlatformEvent intakeEvent : intakeEvents) {
Expand Down Expand Up @@ -338,8 +338,7 @@ void testNGenValueForGenesisEvent() {
final Configuration configuration =
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, mock(IntakeEventCounter.class));
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, mock(IntakeEventCounter.class));

final List<PlatformEvent> unorphanedEvents = orphanBuffer.handleEvent(genesisEvent);
assertThat(unorphanedEvents.size())
Expand Down Expand Up @@ -378,8 +377,7 @@ void testNGenValueWithAncientParents() {
final Configuration configuration =
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, mock(IntakeEventCounter.class));
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, mock(IntakeEventCounter.class));
orphanBuffer.setEventWindow(eventWindow);

final List<PlatformEvent> unorphanedEvents = new ArrayList<>();
Expand Down Expand Up @@ -432,8 +430,7 @@ void testNGenValueWithAncientAndNonAncientParents() {
final Configuration configuration =
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, mock(IntakeEventCounter.class));
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, mock(IntakeEventCounter.class));
orphanBuffer.setEventWindow(eventWindow);

final List<PlatformEvent> unorphanedEvents = new ArrayList<>();
Expand Down Expand Up @@ -514,8 +511,7 @@ void testNGenValueWithNonAncientParents() {
final Configuration configuration =
ConfigurationBuilder.create().autoDiscoverExtensions().build();
final Metrics metrics = new NoOpMetrics();
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(configuration, metrics, mock(IntakeEventCounter.class));
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(metrics, mock(IntakeEventCounter.class));
orphanBuffer.setEventWindow(eventWindow);

final List<PlatformEvent> unorphanedEvents = new ArrayList<>(orphanBuffer.handleEvent(node0AncientEvent));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,8 @@ void filterLikelyDuplicatesTest() {

private static void assertTopologicalOrder(
final PlatformContext platformContext, final List<PlatformEvent> events) {
final DefaultOrphanBuffer orphanBuffer = new DefaultOrphanBuffer(
platformContext.getConfiguration(), platformContext.getMetrics(), new NoOpIntakeEventCounter());
final DefaultOrphanBuffer orphanBuffer =
new DefaultOrphanBuffer(platformContext.getMetrics(), new NoOpIntakeEventCounter());
orphanBuffer.setEventWindow(new EventWindow(1, 1, events.getFirst().getBirthRound(), 1));
// Verify topological ordering.
for (final PlatformEvent event : events) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,7 @@ public TestIntake(@NonNull final PlatformContext platformContext, @NonNull final
new PassThroughWiring(model, "PlatformEvent", "postHashCollector", TaskSchedulerType.DIRECT);

final IntakeEventCounter intakeEventCounter = new NoOpIntakeEventCounter();
final OrphanBuffer orphanBuffer = new DefaultOrphanBuffer(
platformContext.getConfiguration(), platformContext.getMetrics(), intakeEventCounter);
final OrphanBuffer orphanBuffer = new DefaultOrphanBuffer(platformContext.getMetrics(), intakeEventCounter);
orphanBufferWiring = new ComponentWiring<>(model, OrphanBuffer.class, scheduler("orphanBuffer"));
orphanBufferWiring.bind(orphanBuffer);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,8 +196,7 @@ private StandardGraphGenerator(final StandardGraphGenerator that, final long see
private void initializeInternalConsensus() {
consensus = new ConsensusImpl(platformContext, new NoOpConsensusMetrics(), roster);
linker = new SimpleLinker();
orphanBuffer = new DefaultOrphanBuffer(
platformContext.getConfiguration(), platformContext.getMetrics(), mock(IntakeEventCounter.class));
orphanBuffer = new DefaultOrphanBuffer(platformContext.getMetrics(), mock(IntakeEventCounter.class));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ public TestGuiSource(
this.eventStorage = new GuiEventStorage(platformContext.getConfiguration(), roster);
this.guiSource = new StandardGuiSource(roster, eventStorage);
this.eventProvider = eventProvider;
this.orphanBuffer = new DefaultOrphanBuffer(
platformContext.getConfiguration(), platformContext.getMetrics(), new NoOpIntakeEventCounter());
this.orphanBuffer = new DefaultOrphanBuffer(platformContext.getMetrics(), new NoOpIntakeEventCounter());
}

public void runGui() {
Expand Down
Loading