-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
52 changed files
with
429 additions
and
31 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
|
@@ -189,3 +189,4 @@ $RECYCLE.BIN/ | |
# Node | ||
node_modules | ||
package-lock.json | ||
/src/jmh/java/generated/ |
This file contains 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
116 changes: 116 additions & 0 deletions
116
src/jmh/java/io/libp2p/pubsub/gossip/GossipScoreBenchmark.java
This file contains 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,116 @@ | ||
package io.libp2p.pubsub.gossip; | ||
|
||
import io.libp2p.core.PeerId; | ||
import io.libp2p.core.multiformats.Multiaddr; | ||
import io.libp2p.core.multiformats.Protocol; | ||
import io.libp2p.pubsub.DefaultPubsubMessage; | ||
import io.libp2p.tools.schedulers.ControlledExecutorServiceImpl; | ||
import io.libp2p.tools.schedulers.TimeController; | ||
import io.libp2p.tools.schedulers.TimeControllerImpl; | ||
import org.openjdk.jmh.annotations.*; | ||
import org.openjdk.jmh.infra.Blackhole; | ||
import pubsub.pb.Rpc; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.TimeUnit; | ||
import java.util.function.Function; | ||
import java.util.stream.Collectors; | ||
import java.util.stream.IntStream; | ||
import java.util.stream.Stream; | ||
|
||
@State(Scope.Thread) | ||
@Fork(5) | ||
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS) | ||
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS) | ||
public class GossipScoreBenchmark { | ||
|
||
private final int peerCount = 5000; | ||
private final int connectedCount = 2000; | ||
private final int topicCount = 128; | ||
|
||
private final List<String> topics = IntStream | ||
.range(0, topicCount) | ||
.mapToObj(i -> "Topic-" + i) | ||
.collect(Collectors.toList()); | ||
|
||
private final List<PeerId> peerIds = Stream.generate(PeerId::random).limit(peerCount).collect(Collectors.toList()); | ||
private final List<Multiaddr> peerAddresses = IntStream | ||
.range(0, peerCount) | ||
.mapToObj(idx -> | ||
Multiaddr.empty() | ||
.withComponent(Protocol.IP4, new byte[]{(byte) (idx >>> 8 & 0xFF), (byte) (idx & 0xFF), 0, 0}) | ||
.withComponent(Protocol.TCP, new byte[]{0x23, 0x28})) | ||
.collect(Collectors.toList()); | ||
|
||
private final TimeController timeController = new TimeControllerImpl(); | ||
private final ControlledExecutorServiceImpl controlledExecutor = new ControlledExecutorServiceImpl(); | ||
private final GossipScoreParams gossipScoreParams; | ||
private final DefaultGossipScore score; | ||
|
||
public GossipScoreBenchmark() { | ||
Map<String, GossipTopicScoreParams> topicParamMap = topics.stream() | ||
.collect(Collectors.toMap(Function.identity(), __ -> new GossipTopicScoreParams())); | ||
GossipTopicsScoreParams gossipTopicsScoreParams = new GossipTopicsScoreParams(new GossipTopicScoreParams(), topicParamMap); | ||
|
||
gossipScoreParams = new GossipScoreParams(new GossipPeerScoreParams(), gossipTopicsScoreParams, 0, 0, 0, 0, 0); | ||
controlledExecutor.setTimeController(timeController); | ||
score = new DefaultGossipScore(gossipScoreParams, controlledExecutor, timeController::getTime); | ||
|
||
for (int i = 0; i < peerCount; i++) { | ||
PeerId peerId = peerIds.get(i); | ||
score.notifyConnected(peerId, peerAddresses.get(i)); | ||
for (String topic : topics) { | ||
notifyUnseenMessage(peerId, topic); | ||
} | ||
} | ||
|
||
for (int i = connectedCount; i < peerCount; i++) { | ||
score.notifyDisconnected(peerIds.get(i)); | ||
} | ||
} | ||
|
||
private void notifyUnseenMessage(PeerId peerId, String topic) { | ||
Rpc.Message message = Rpc.Message.newBuilder() | ||
.addTopicIDs(topic) | ||
.build(); | ||
score.notifyUnseenValidMessage(peerId, new DefaultPubsubMessage(message)); | ||
} | ||
|
||
@Benchmark | ||
public void scoresDelay0(Blackhole bh) { | ||
for (int i = 0; i < connectedCount; i++) { | ||
double s = score.score(peerIds.get(i)); | ||
bh.consume(s); | ||
} | ||
} | ||
|
||
@Benchmark | ||
public void scoresDelay100(Blackhole bh) { | ||
timeController.addTime(100); | ||
|
||
for (int i = 0; i < connectedCount; i++) { | ||
double s = score.score(peerIds.get(i)); | ||
bh.consume(s); | ||
} | ||
} | ||
|
||
@Benchmark | ||
public void scoresDelay10000(Blackhole bh) { | ||
timeController.addTime(10000); | ||
|
||
for (int i = 0; i < connectedCount; i++) { | ||
double s = score.score(peerIds.get(i)); | ||
bh.consume(s); | ||
} | ||
} | ||
|
||
/** | ||
* Uncomment for debugging | ||
*/ | ||
// public static void main(String[] args) { | ||
// GossipScoreBenchmark benchmark = new GossipScoreBenchmark(); | ||
// Blackhole blackhole = new Blackhole("Today's password is swordfish. I understand instantiating Blackholes directly is dangerous."); | ||
// benchmark.scoresDelay0(blackhole); | ||
// } | ||
} |
This file contains 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,90 @@ | ||
package io.libp2p.etc.types | ||
|
||
fun <TValue1, TValue2> mutableMultiBiMap(): MutableMultiBiMap<TValue1, TValue2> = HashSetMultiBiMap() | ||
|
||
/** | ||
* Associates values of type [TFirst] to a set of values of type [TSecond] | ||
* and back: associates values of type [TSecond] to a set of values of type [TFirst] | ||
*/ | ||
interface MultiBiMap<TFirst, TSecond> { | ||
|
||
fun getByFirst(first: TFirst): Set<TSecond> | ||
fun getBySecond(second: TSecond): Set<TFirst> | ||
|
||
fun valuesFirst(): Set<TFirst> | ||
fun valuesSecond(): Set<TSecond> | ||
|
||
fun asFirstToSecondMap(): Map<TFirst, Set<TSecond>> = valuesFirst().associateWith { getByFirst(it) } | ||
fun asSecondToFirstMap(): Map<TSecond, Set<TFirst>> = valuesSecond().associateWith { getBySecond(it) } | ||
} | ||
|
||
interface MutableMultiBiMap<TFirst, TSecond> : MultiBiMap<TFirst, TSecond> { | ||
|
||
fun add(first: TFirst, second: TSecond) | ||
|
||
fun remove(first: TFirst, second: TSecond) | ||
fun removeAllByFirst(first: TFirst) | ||
fun removeAllBySecond(second: TSecond) | ||
} | ||
|
||
internal class HashSetMultiBiMap<TFirst, TSecond> : MutableMultiBiMap<TFirst, TSecond> { | ||
private val firstToSecondMap = mutableMapOf<TFirst, MutableSet<TSecond>>() | ||
private val secondToFirstMap = mutableMapOf<TSecond, MutableSet<TFirst>>() | ||
|
||
override fun getByFirst(first: TFirst): Set<TSecond> = firstToSecondMap[first] ?: emptySet() | ||
override fun getBySecond(second: TSecond): Set<TFirst> = secondToFirstMap[second] ?: emptySet() | ||
override fun valuesFirst(): Set<TFirst> = firstToSecondMap.keys | ||
override fun valuesSecond(): Set<TSecond> = secondToFirstMap.keys | ||
override fun asFirstToSecondMap(): Map<TFirst, Set<TSecond>> = firstToSecondMap | ||
override fun asSecondToFirstMap(): Map<TSecond, Set<TFirst>> = secondToFirstMap | ||
|
||
override fun add(first: TFirst, second: TSecond) { | ||
firstToSecondMap.computeIfAbsent(first) { mutableSetOf() } += second | ||
secondToFirstMap.computeIfAbsent(second) { mutableSetOf() } += first | ||
} | ||
|
||
private fun removeFromFirstToSecondMap(first: TFirst, second: TSecond) { | ||
firstToSecondMap.compute(first) { _, curSecondValues -> | ||
if (curSecondValues != null) { | ||
curSecondValues -= second | ||
if (curSecondValues.isNotEmpty()) { | ||
curSecondValues | ||
} else { | ||
null | ||
} | ||
} else { | ||
null | ||
} | ||
} | ||
} | ||
|
||
private fun removeFromSecondToFirstMap(first: TFirst, second: TSecond) { | ||
secondToFirstMap.compute(second) { _, curFirstValues -> | ||
if (curFirstValues != null) { | ||
curFirstValues -= first | ||
if (curFirstValues.isNotEmpty()) { | ||
curFirstValues | ||
} else { | ||
null | ||
} | ||
} else { | ||
null | ||
} | ||
} | ||
} | ||
|
||
override fun remove(first: TFirst, second: TSecond) { | ||
removeFromFirstToSecondMap(first, second) | ||
removeFromSecondToFirstMap(first, second) | ||
} | ||
|
||
override fun removeAllByFirst(first: TFirst) { | ||
val removedSecondValues = firstToSecondMap.remove(first) ?: emptySet() | ||
removedSecondValues.forEach { removeFromSecondToFirstMap(first, it) } | ||
} | ||
|
||
override fun removeAllBySecond(second: TSecond) { | ||
val removedFirstValues = secondToFirstMap.remove(second) ?: emptySet() | ||
removedFirstValues.forEach { removeFromFirstToSecondMap(it, second) } | ||
} | ||
} |
This file contains 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 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 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.