Ported ActorBenchmark to typed (#24815)
This commit is contained in:
parent
dd884117c1
commit
30423e3b84
2 changed files with 212 additions and 0 deletions
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.actor.typed
|
||||||
|
|
||||||
|
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||||
|
|
||||||
|
import com.typesafe.config.ConfigFactory
|
||||||
|
import org.openjdk.jmh.annotations._
|
||||||
|
|
||||||
|
import scala.concurrent.Await
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
import akka.actor.typed.scaladsl.AskPattern._
|
||||||
|
|
||||||
|
object TypedActorBenchmark {
|
||||||
|
// Constants because they are used in annotations
|
||||||
|
final val threads = 8 // update according to cpu
|
||||||
|
final val numMessagesPerActorPair = 1000000 // messages per actor pair
|
||||||
|
|
||||||
|
final val numActors = 512
|
||||||
|
final val totalMessages = numMessagesPerActorPair * numActors / 2
|
||||||
|
final val timeout = 30.seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
@State(Scope.Benchmark)
|
||||||
|
@BenchmarkMode(Array(Mode.Throughput))
|
||||||
|
@Fork(1)
|
||||||
|
@Threads(1)
|
||||||
|
@Warmup(iterations = 10, time = 5, timeUnit = TimeUnit.SECONDS, batchSize = 1)
|
||||||
|
@Measurement(iterations = 10, time = 15, timeUnit = TimeUnit.SECONDS, batchSize = 1)
|
||||||
|
class TypedActorBenchmark {
|
||||||
|
import TypedActorBenchmark._
|
||||||
|
import TypedBenchmarkActors._
|
||||||
|
|
||||||
|
@Param(Array("50"))
|
||||||
|
var tpt = 0
|
||||||
|
|
||||||
|
@Param(Array("50"))
|
||||||
|
var batchSize = 0
|
||||||
|
|
||||||
|
@Param(Array("akka.actor.ManyToOneArrayMailbox")) // @Param(Array("akka.dispatch.SingleConsumerOnlyUnboundedMailbox", "akka.actor.ManyToOneArrayMailbox"))
|
||||||
|
var mailbox = ""
|
||||||
|
|
||||||
|
@Param(Array("fjp-dispatcher")) // @Param(Array("fjp-dispatcher", "affinity-dispatcher"))
|
||||||
|
var dispatcher = ""
|
||||||
|
|
||||||
|
implicit var system: ActorSystem[Start] = _
|
||||||
|
|
||||||
|
implicit val askTimeout = akka.util.Timeout(timeout)
|
||||||
|
implicit def scheduler = system.scheduler
|
||||||
|
|
||||||
|
@Setup(Level.Trial)
|
||||||
|
def setup(): Unit = {
|
||||||
|
akka.actor.BenchmarkActors.requireRightNumberOfCores(threads)
|
||||||
|
system = ActorSystem(
|
||||||
|
TypedBenchmarkActors.echoActorsSupervisor(numMessagesPerActorPair, numActors, dispatcher, batchSize, timeout),
|
||||||
|
"TypedActorBenchmark",
|
||||||
|
ConfigFactory.parseString(
|
||||||
|
s"""
|
||||||
|
akka.actor {
|
||||||
|
|
||||||
|
default-mailbox.mailbox-capacity = 512
|
||||||
|
|
||||||
|
fjp-dispatcher {
|
||||||
|
executor = "fork-join-executor"
|
||||||
|
fork-join-executor {
|
||||||
|
parallelism-min = $threads
|
||||||
|
parallelism-factor = 1.0
|
||||||
|
parallelism-max = $threads
|
||||||
|
}
|
||||||
|
throughput = $tpt
|
||||||
|
mailbox-type = "$mailbox"
|
||||||
|
}
|
||||||
|
affinity-dispatcher {
|
||||||
|
executor = "affinity-pool-executor"
|
||||||
|
affinity-pool-executor {
|
||||||
|
parallelism-min = $threads
|
||||||
|
parallelism-factor = 1.0
|
||||||
|
parallelism-max = $threads
|
||||||
|
task-queue-size = 512
|
||||||
|
idle-cpu-level = 5
|
||||||
|
fair-work-distribution.threshold = 2048
|
||||||
|
}
|
||||||
|
throughput = $tpt
|
||||||
|
mailbox-type = "$mailbox"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
@TearDown(Level.Trial)
|
||||||
|
def shutdown(): Unit = {
|
||||||
|
system.terminate()
|
||||||
|
Await.ready(system.whenTerminated, 15.seconds)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Benchmark
|
||||||
|
@OperationsPerInvocation(totalMessages)
|
||||||
|
def echo(): Unit = {
|
||||||
|
Await.result(system ? Start, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2009-2018 Lightbend Inc. <https://www.lightbend.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package akka.actor.typed
|
||||||
|
|
||||||
|
import java.util.concurrent.{ CountDownLatch, TimeUnit }
|
||||||
|
|
||||||
|
import akka.Done
|
||||||
|
import akka.actor.typed.scaladsl.{ Behaviors, MutableBehavior }
|
||||||
|
import akka.actor.typed.scaladsl.{ ActorContext ⇒ SActorContext }
|
||||||
|
|
||||||
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
object TypedBenchmarkActors {
|
||||||
|
|
||||||
|
// to avoid benchmark to be dominated by allocations of message
|
||||||
|
// we pass the respondTo actor ref into the behavior
|
||||||
|
final case object Message
|
||||||
|
|
||||||
|
private def echoBehavior(respondTo: ActorRef[Message.type]): Behavior[Message.type] = Behaviors.receive { (ctx, msg) ⇒
|
||||||
|
respondTo ! Message
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
|
||||||
|
private def echoSender(messagesPerPair: Int, onDone: ActorRef[Done], batchSize: Int, childProps: Props): Behavior[Message.type] =
|
||||||
|
Behaviors.setup { ctx ⇒
|
||||||
|
val echo = ctx.spawn(echoBehavior(ctx.self), "echo", childProps)
|
||||||
|
var left = messagesPerPair / 2
|
||||||
|
var batch = 0
|
||||||
|
|
||||||
|
def sendBatch(): Boolean = {
|
||||||
|
if (left > 0) {
|
||||||
|
var i = 0
|
||||||
|
while (i < batchSize) {
|
||||||
|
echo ! Message
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
left -= batchSize
|
||||||
|
batch = batchSize
|
||||||
|
true
|
||||||
|
} else
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
Behaviors.receiveMessage { msg ⇒
|
||||||
|
batch -= 1
|
||||||
|
if (batch <= 0 && !sendBatch()) {
|
||||||
|
onDone ! Done
|
||||||
|
Behaviors.stopped
|
||||||
|
} else {
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case class Start(respondTo: ActorRef[Completed])
|
||||||
|
case class Completed(startNanoTime: Long)
|
||||||
|
|
||||||
|
def echoActorsSupervisor(numMessagesPerActorPair: Int, numActors: Int, dispatcher: String, batchSize: Int,
|
||||||
|
shutdownTimeout: FiniteDuration): Behavior[Start] =
|
||||||
|
Behaviors.receive { (ctx, msg) ⇒
|
||||||
|
msg match {
|
||||||
|
case Start(respondTo) ⇒
|
||||||
|
// note: no protection against accidentally running bench sessions in paralell
|
||||||
|
val sessionBehavior = startEchoBenchSession(numMessagesPerActorPair, numActors, dispatcher, batchSize, respondTo)
|
||||||
|
ctx.spawnAnonymous(sessionBehavior)
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private def startEchoBenchSession(messagesPerPair: Int, numActors: Int, dispatcher: String,
|
||||||
|
batchSize: Int, respondTo: ActorRef[Completed]): Behavior[Unit] = {
|
||||||
|
|
||||||
|
val numPairs = numActors / 2
|
||||||
|
|
||||||
|
Behaviors.setup[Any] { ctx ⇒
|
||||||
|
val props = Props.empty.withDispatcherFromConfig("akka.actor." + dispatcher)
|
||||||
|
val pairs = (1 to numPairs).map { _ ⇒
|
||||||
|
ctx.spawnAnonymous(echoSender(messagesPerPair, ctx.self.narrow[Done], batchSize, props), props)
|
||||||
|
}
|
||||||
|
val startNanoTime = System.nanoTime()
|
||||||
|
pairs.foreach(_ ! Message)
|
||||||
|
var interactionsLeft = numPairs
|
||||||
|
Behaviors.receiveMessage {
|
||||||
|
case Done ⇒
|
||||||
|
interactionsLeft -= 1
|
||||||
|
if (interactionsLeft == 0) {
|
||||||
|
val totalNumMessages = numPairs * messagesPerPair
|
||||||
|
printProgress(totalNumMessages, numActors, startNanoTime)
|
||||||
|
respondTo ! Completed(startNanoTime)
|
||||||
|
Behaviors.stopped
|
||||||
|
} else {
|
||||||
|
Behaviors.same
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}.narrow[Unit]
|
||||||
|
}
|
||||||
|
|
||||||
|
private def printProgress(totalMessages: Long, numActors: Int, startNanoTime: Long) = {
|
||||||
|
val durationMicros = (System.nanoTime() - startNanoTime) / 1000
|
||||||
|
println(f" $totalMessages messages by $numActors actors took ${durationMicros / 1000} ms, " +
|
||||||
|
f"${totalMessages.toDouble / durationMicros}%,.2f M msg/s")
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue