Skip to content

Commit e9d2a29

Browse files
feat(ReconnectBench): fuzz slow I/O delay emulation
Signed-off-by: Anthony Petrov <[email protected]>
1 parent 682841e commit e9d2a29

File tree

6 files changed

+129
-9
lines changed

6 files changed

+129
-9
lines changed

platform-sdk/swirlds-benchmarks/src/jmh/java/com/swirlds/benchmark/ReconnectBench.java

+22-1
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,27 @@ public class ReconnectBench extends VirtualMapBaseBench {
7171
@Param({"0"})
7272
public long delayStorageMicroseconds;
7373

74+
/**
75+
* A percentage fuzz range for the delayStorageMicroseconds values,
76+
* e.g. 0.15 for a -15%..+15% range around the value.
77+
*/
78+
@Param({"0.15"})
79+
public double delayStorageFuzzRangePercent;
80+
7481
/**
7582
* Emulated delay for serializeMessage() calls in both Teaching- and Learning-Synchronizers,
7683
* or zero for no delay. This emulates slow network I/O when sending data.
7784
*/
7885
@Param({"0"})
7986
public long delayNetworkMicroseconds;
8087

88+
/**
89+
* A percentage fuzz range for the delayNetworkMicroseconds values,
90+
* e.g. 0.15 for a -15%..+15% range around the value.
91+
*/
92+
@Param({"0.15"})
93+
public double delayNetworkFuzzRangePercent;
94+
8195
private VirtualMap<BenchmarkKey, BenchmarkValue> teacherMap;
8296
private VirtualMap<BenchmarkKey, BenchmarkValue> learnerMap;
8397
private MerkleInternal teacherTree;
@@ -214,6 +228,13 @@ public void tearDownInvocation() throws Exception {
214228
@Benchmark
215229
public void reconnect() throws Exception {
216230
node = MerkleBenchmarkUtils.hashAndTestSynchronization(
217-
learnerTree, teacherTree, delayStorageMicroseconds, delayNetworkMicroseconds, configuration);
231+
learnerTree,
232+
teacherTree,
233+
randomSeed,
234+
delayStorageMicroseconds,
235+
delayStorageFuzzRangePercent,
236+
delayNetworkMicroseconds,
237+
delayNetworkFuzzRangePercent,
238+
configuration);
218239
}
219240
}

platform-sdk/swirlds-benchmarks/src/jmh/java/com/swirlds/benchmark/reconnect/MerkleBenchmarkUtils.java

+15
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@ public static MerkleInternal createTreeForMap(final VirtualMap<BenchmarkKey, Ben
5555
public static <T extends MerkleNode> T hashAndTestSynchronization(
5656
final MerkleNode startingTree,
5757
final MerkleNode desiredTree,
58+
final long randomSeed,
5859
final long delayStorageMicroseconds,
60+
final double delayStorageFuzzRangePercent,
5961
final long delayNetworkMicroseconds,
62+
final double delayNetworkFuzzRangePercent,
6063
final Configuration configuration)
6164
throws Exception {
6265
System.out.println("------------");
@@ -74,8 +77,11 @@ public static <T extends MerkleNode> T hashAndTestSynchronization(
7477
return testSynchronization(
7578
startingTree,
7679
desiredTree,
80+
randomSeed,
7781
delayStorageMicroseconds,
82+
delayStorageFuzzRangePercent,
7883
delayNetworkMicroseconds,
84+
delayNetworkFuzzRangePercent,
7985
configuration,
8086
reconnectConfig);
8187
}
@@ -87,8 +93,11 @@ public static <T extends MerkleNode> T hashAndTestSynchronization(
8793
private static <T extends MerkleNode> T testSynchronization(
8894
final MerkleNode startingTree,
8995
final MerkleNode desiredTree,
96+
final long randomSeed,
9097
final long delayStorageMicroseconds,
98+
final double delayStorageFuzzRangePercent,
9199
final long delayNetworkMicroseconds,
100+
final double delayNetworkFuzzRangePercent,
92101
final Configuration configuration,
93102
final ReconnectConfig reconnectConfig)
94103
throws Exception {
@@ -132,8 +141,11 @@ private static <T extends MerkleNode> T testSynchronization(
132141
streams.getLearnerInput(),
133142
streams.getLearnerOutput(),
134143
startingTree,
144+
randomSeed,
135145
delayStorageMicroseconds,
146+
delayStorageFuzzRangePercent,
136147
delayNetworkMicroseconds,
148+
delayNetworkFuzzRangePercent,
137149
() -> {
138150
try {
139151
streams.disconnect();
@@ -148,8 +160,11 @@ private static <T extends MerkleNode> T testSynchronization(
148160
streams.getTeacherInput(),
149161
streams.getTeacherOutput(),
150162
desiredTree,
163+
randomSeed,
151164
delayStorageMicroseconds,
165+
delayStorageFuzzRangePercent,
152166
delayNetworkMicroseconds,
167+
delayNetworkFuzzRangePercent,
153168
() -> {
154169
try {
155170
streams.disconnect();

platform-sdk/swirlds-benchmarks/src/jmh/java/com/swirlds/benchmark/reconnect/lag/BenchmarkSlowAsyncOutputStream.java

+17-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.swirlds.common.threading.pool.StandardWorkGroup;
2525
import java.io.IOException;
2626
import java.time.Duration;
27+
import java.util.Random;
2728

2829
/**
2930
* This variant of the async output stream introduces an extra delay for every single
@@ -32,18 +33,28 @@
3233
*/
3334
public class BenchmarkSlowAsyncOutputStream<T extends SelfSerializable> extends AsyncOutputStream<T> {
3435

35-
private final long delayStorageMicroseconds;
36-
private final long delayNetworkMicroseconds;
36+
private final LongFuzzer delayStorageMicrosecondsFuzzer;
37+
private final LongFuzzer delayNetworkMicrosecondsFuzzer;
3738

3839
public BenchmarkSlowAsyncOutputStream(
3940
final SerializableDataOutputStream out,
4041
final StandardWorkGroup workGroup,
42+
final long randomSeed,
4143
final long delayStorageMicroseconds,
44+
final double delayStorageFuzzRangePercent,
4245
final long delayNetworkMicroseconds,
46+
final double delayNetworkFuzzRangePercent,
4347
final ReconnectConfig reconnectConfig) {
4448
super(out, workGroup, reconnectConfig);
45-
this.delayStorageMicroseconds = delayStorageMicroseconds;
46-
this.delayNetworkMicroseconds = delayNetworkMicroseconds;
49+
50+
// Note that we use randomSeed and -randomSeed for the two fuzzers
51+
// to ensure that they don't end up returning the exact same
52+
// (relatively, that is, in percentages) delay
53+
// for both the storage and network.
54+
delayStorageMicrosecondsFuzzer =
55+
new LongFuzzer(delayStorageMicroseconds, new Random(randomSeed), delayStorageFuzzRangePercent);
56+
delayNetworkMicrosecondsFuzzer =
57+
new LongFuzzer(delayNetworkMicroseconds, new Random(-randomSeed), delayNetworkFuzzRangePercent);
4758
}
4859

4960
/**
@@ -54,7 +65,7 @@ public void sendAsync(final T message) throws InterruptedException {
5465
if (!isAlive()) {
5566
throw new MerkleSynchronizationException("Messages can not be sent after close has been called.");
5667
}
57-
sleepMicros(delayStorageMicroseconds);
68+
sleepMicros(delayStorageMicrosecondsFuzzer.next());
5869
getOutgoingMessages().put(message);
5970
}
6071

@@ -63,7 +74,7 @@ public void sendAsync(final T message) throws InterruptedException {
6374
*/
6475
@Override
6576
protected void serializeMessage(final T message) throws IOException {
66-
sleepMicros(delayNetworkMicroseconds);
77+
sleepMicros(delayNetworkMicrosecondsFuzzer.next());
6778
message.serialize(getOutputStream());
6879
}
6980

platform-sdk/swirlds-benchmarks/src/jmh/java/com/swirlds/benchmark/reconnect/lag/BenchmarkSlowLearningSynchronizer.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,11 @@
3333
*/
3434
public class BenchmarkSlowLearningSynchronizer extends LearningSynchronizer {
3535

36+
private final long randomSeed;
3637
private final long delayStorageMicroseconds;
38+
private final double delayStorageFuzzRangePercent;
3739
private final long delayNetworkMicroseconds;
40+
private final double delayNetworkFuzzRangePercent;
3841

3942
/**
4043
* Create a new learning synchronizer with simulated latency.
@@ -43,14 +46,20 @@ public BenchmarkSlowLearningSynchronizer(
4346
final MerkleDataInputStream in,
4447
final MerkleDataOutputStream out,
4548
final MerkleNode root,
49+
final long randomSeed,
4650
final long delayStorageMicroseconds,
51+
final double delayStorageFuzzRangePercent,
4752
final long delayNetworkMicroseconds,
53+
final double delayNetworkFuzzRangePercent,
4854
final Runnable breakConnection,
4955
final ReconnectConfig reconnectConfig) {
5056
super(getStaticThreadManager(), in, out, root, breakConnection, reconnectConfig);
5157

58+
this.randomSeed = randomSeed;
5259
this.delayStorageMicroseconds = delayStorageMicroseconds;
60+
this.delayStorageFuzzRangePercent = delayStorageFuzzRangePercent;
5361
this.delayNetworkMicroseconds = delayNetworkMicroseconds;
62+
this.delayNetworkFuzzRangePercent = delayNetworkFuzzRangePercent;
5463
}
5564

5665
/**
@@ -60,6 +69,13 @@ public BenchmarkSlowLearningSynchronizer(
6069
protected AsyncOutputStream<QueryResponse> buildOutputStream(
6170
final StandardWorkGroup workGroup, final SerializableDataOutputStream out) {
6271
return new BenchmarkSlowAsyncOutputStream<>(
63-
out, workGroup, delayStorageMicroseconds, delayNetworkMicroseconds, reconnectConfig);
72+
out,
73+
workGroup,
74+
randomSeed,
75+
delayStorageMicroseconds,
76+
delayStorageFuzzRangePercent,
77+
delayNetworkMicroseconds,
78+
delayNetworkFuzzRangePercent,
79+
reconnectConfig);
6480
}
6581
}

platform-sdk/swirlds-benchmarks/src/jmh/java/com/swirlds/benchmark/reconnect/lag/BenchmarkSlowTeachingSynchronizer.java

+18-1
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,11 @@
3636
*/
3737
public class BenchmarkSlowTeachingSynchronizer extends TeachingSynchronizer {
3838

39+
private final long randomSeed;
3940
private final long delayStorageMicroseconds;
41+
private final double delayStorageFuzzRangePercent;
4042
private final long delayNetworkMicroseconds;
43+
private final double delayNetworkFuzzRangePercent;
4144

4245
/**
4346
* Create a new teaching synchronizer with simulated latency.
@@ -47,8 +50,11 @@ public BenchmarkSlowTeachingSynchronizer(
4750
final MerkleDataInputStream in,
4851
final MerkleDataOutputStream out,
4952
final MerkleNode root,
53+
final long randomSeed,
5054
final long delayStorageMicroseconds,
55+
final double delayStorageFuzzRangePercent,
5156
final long delayNetworkMicroseconds,
57+
final double delayNetworkFuzzRangePercent,
5258
final Runnable breakConnection,
5359
final ReconnectConfig reconnectConfig) {
5460
super(
@@ -60,8 +66,12 @@ public BenchmarkSlowTeachingSynchronizer(
6066
root,
6167
breakConnection,
6268
reconnectConfig);
69+
70+
this.randomSeed = randomSeed;
6371
this.delayStorageMicroseconds = delayStorageMicroseconds;
72+
this.delayStorageFuzzRangePercent = delayStorageFuzzRangePercent;
6473
this.delayNetworkMicroseconds = delayNetworkMicroseconds;
74+
this.delayNetworkFuzzRangePercent = delayNetworkFuzzRangePercent;
6575
}
6676

6777
/**
@@ -71,6 +81,13 @@ public BenchmarkSlowTeachingSynchronizer(
7181
protected <T> AsyncOutputStream<Lesson<T>> buildOutputStream(
7282
final StandardWorkGroup workGroup, final SerializableDataOutputStream out) {
7383
return new BenchmarkSlowAsyncOutputStream<>(
74-
out, workGroup, delayStorageMicroseconds, delayNetworkMicroseconds, reconnectConfig);
84+
out,
85+
workGroup,
86+
randomSeed,
87+
delayStorageMicroseconds,
88+
delayStorageFuzzRangePercent,
89+
delayNetworkMicroseconds,
90+
delayNetworkFuzzRangePercent,
91+
reconnectConfig);
7592
}
7693
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/*
2+
* Copyright (C) 2024 Hedera Hashgraph, LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.swirlds.benchmark.reconnect.lag;
18+
19+
import java.util.Random;
20+
21+
/**
22+
* A generator of random long values in a given percentage range around a given base value.
23+
*
24+
* @param value a base value
25+
* @param random a Random instance, or null for no fuzz
26+
* @param rangePercent a rangePercent, e.g. 0.15 for a -15%..+15% range around the base value.
27+
*/
28+
public record LongFuzzer(long value, Random random, double rangePercent) {
29+
/**
30+
* Returns the next long value in the range -rangePercent..+rangePercent around the base value.
31+
* @return the next long value
32+
*/
33+
public long next() {
34+
if (random == null || rangePercent == .0) return value;
35+
36+
// Generate a random fuzz percentage in the range -rangePercent..+rangePercent, e.g. -0.15..+0.15 for 15% range
37+
final double fuzzPercent = random.nextDouble(rangePercent * 2.) - rangePercent;
38+
return (long) ((double) value * (1. + fuzzPercent));
39+
}
40+
}

0 commit comments

Comments
 (0)