2016-04-19 17:38:26 +02:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2016 Lightbend Inc. <http://www.lightbend.com>
|
|
|
|
|
*/
|
|
|
|
|
package akka.remote.artery
|
|
|
|
|
|
2016-12-30 12:50:24 +01:00
|
|
|
import java.io.FileOutputStream
|
2016-06-05 19:11:52 +02:00
|
|
|
import java.nio.ByteBuffer
|
2016-12-30 12:50:24 +01:00
|
|
|
import java.text.SimpleDateFormat
|
|
|
|
|
import java.util.Date
|
2016-04-19 17:38:26 +02:00
|
|
|
import java.util.concurrent.Executors
|
|
|
|
|
import java.util.concurrent.TimeUnit.NANOSECONDS
|
2016-09-16 15:12:40 +02:00
|
|
|
|
2016-06-24 11:21:21 +02:00
|
|
|
import scala.concurrent.duration._
|
2016-04-19 17:38:26 +02:00
|
|
|
import akka.actor._
|
2016-12-30 12:50:24 +01:00
|
|
|
import akka.remote.{ RARP, RemoteActorRefProvider, RemotingMultiNodeSpec }
|
2016-04-19 17:38:26 +02:00
|
|
|
import akka.remote.testconductor.RoleName
|
|
|
|
|
import akka.remote.testkit.MultiNodeConfig
|
|
|
|
|
import akka.remote.testkit.MultiNodeSpec
|
2016-06-24 11:21:21 +02:00
|
|
|
import akka.remote.testkit.PerfFlamesSupport
|
2016-04-19 17:38:26 +02:00
|
|
|
import akka.remote.testkit.STMultiNodeSpec
|
2016-06-05 19:11:52 +02:00
|
|
|
import akka.serialization.ByteBufferSerializer
|
|
|
|
|
import akka.serialization.SerializerWithStringManifest
|
2016-04-19 17:38:26 +02:00
|
|
|
import akka.testkit._
|
|
|
|
|
import com.typesafe.config.ConfigFactory
|
2016-07-04 15:59:44 +02:00
|
|
|
import akka.remote.artery.compress.CompressionProtocol.Events.ReceivedActorRefCompressionTable
|
2016-04-19 17:38:26 +02:00
|
|
|
|
|
|
|
|
object MaxThroughputSpec extends MultiNodeConfig {
|
|
|
|
|
val first = role("first")
|
|
|
|
|
val second = role("second")
|
|
|
|
|
|
|
|
|
|
val barrierTimeout = 5.minutes
|
|
|
|
|
|
|
|
|
|
commonConfig(debugConfig(on = false).withFallback(
|
|
|
|
|
ConfigFactory.parseString(s"""
|
|
|
|
|
# for serious measurements you should increase the totalMessagesFactor (20)
|
|
|
|
|
akka.test.MaxThroughputSpec.totalMessagesFactor = 1.0
|
2016-08-30 14:37:11 +02:00
|
|
|
akka.test.MaxThroughputSpec.real-message = off
|
2016-04-19 17:38:26 +02:00
|
|
|
akka {
|
2016-07-04 15:59:44 +02:00
|
|
|
loglevel = INFO
|
|
|
|
|
log-dead-letters = 1000000
|
2016-06-05 19:11:52 +02:00
|
|
|
# avoid TestEventListener
|
|
|
|
|
loggers = ["akka.event.Logging$$DefaultLogger"]
|
2016-04-19 17:38:26 +02:00
|
|
|
testconductor.barrier-timeout = ${barrierTimeout.toSeconds}s
|
|
|
|
|
actor {
|
2016-06-10 15:04:13 +02:00
|
|
|
provider = remote
|
2016-04-19 17:38:26 +02:00
|
|
|
serialize-creators = false
|
|
|
|
|
serialize-messages = false
|
2016-06-05 19:11:52 +02:00
|
|
|
|
|
|
|
|
serializers {
|
|
|
|
|
test = "akka.remote.artery.MaxThroughputSpec$$TestSerializer"
|
2016-08-30 14:37:11 +02:00
|
|
|
test-message = "akka.remote.artery.TestMessageSerializer"
|
2016-06-05 19:11:52 +02:00
|
|
|
}
|
|
|
|
|
serialization-bindings {
|
|
|
|
|
"akka.remote.artery.MaxThroughputSpec$$FlowControl" = test
|
2016-08-30 14:37:11 +02:00
|
|
|
"akka.remote.artery.TestMessage" = test-message
|
2016-06-05 19:11:52 +02:00
|
|
|
}
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
remote.artery {
|
|
|
|
|
enabled = on
|
2016-06-01 11:56:18 +02:00
|
|
|
|
|
|
|
|
# for serious measurements when running this test on only one machine
|
|
|
|
|
# it is recommended to use external media driver
|
2016-12-20 15:07:57 +01:00
|
|
|
# See akka-remote/src/test/resources/aeron.properties
|
|
|
|
|
# advanced.embedded-media-driver = off
|
|
|
|
|
# advanced.aeron-dir = "target/aeron"
|
|
|
|
|
# on linux, use directory on ram disk, instead
|
|
|
|
|
# advanced.aeron-dir = "/dev/shm/aeron"
|
2016-07-04 16:42:14 +02:00
|
|
|
|
|
|
|
|
advanced.compression {
|
2016-07-04 15:59:44 +02:00
|
|
|
actor-refs.advertisement-interval = 2 second
|
|
|
|
|
manifests.advertisement-interval = 2 second
|
2016-07-04 16:42:14 +02:00
|
|
|
}
|
2016-12-30 12:52:42 +01:00
|
|
|
|
|
|
|
|
advanced {
|
|
|
|
|
inbound-lanes = 2
|
|
|
|
|
# buffer-pool-size = 512
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
akka.remote.default-remote-dispatcher {
|
|
|
|
|
fork-join-executor {
|
|
|
|
|
# parallelism-factor = 0.5
|
|
|
|
|
parallelism-min = 2
|
|
|
|
|
parallelism-max = 2
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
2016-12-30 12:52:42 +01:00
|
|
|
# Set to 10 by default. Might be worthwhile to experiment with.
|
|
|
|
|
# throughput = 100
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
2016-11-23 12:02:36 +01:00
|
|
|
""")).withFallback(RemotingMultiNodeSpec.commonConfig))
|
2016-04-19 17:38:26 +02:00
|
|
|
|
|
|
|
|
case object Run
|
2016-09-09 09:01:15 +02:00
|
|
|
sealed trait Echo extends DeadLetterSuppression with JavaSerializable
|
2016-12-30 12:52:42 +01:00
|
|
|
final case class Start(correspondingReceiver: ActorRef) extends Echo
|
2016-04-19 17:38:26 +02:00
|
|
|
final case object End extends Echo
|
2016-12-30 12:52:42 +01:00
|
|
|
final case class Warmup(msg: AnyRef)
|
2016-09-09 09:01:15 +02:00
|
|
|
final case class EndResult(totalReceived: Long) extends JavaSerializable
|
2016-04-19 17:38:26 +02:00
|
|
|
final case class FlowControl(burstStartTime: Long) extends Echo
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
def receiverProps(reporter: RateReporter, payloadSize: Int, printTaskRunnerMetrics: Boolean, numSenders: Int): Props =
|
|
|
|
|
Props(new Receiver(reporter, payloadSize, printTaskRunnerMetrics, numSenders)).withDispatcher("akka.remote.default-remote-dispatcher")
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
class Receiver(reporter: RateReporter, payloadSize: Int, printTaskRunnerMetrics: Boolean, numSenders: Int) extends Actor {
|
2016-06-05 19:11:52 +02:00
|
|
|
private var c = 0L
|
2016-06-15 15:12:33 +02:00
|
|
|
private val taskRunnerMetrics = new TaskRunnerMetrics(context.system)
|
2016-12-30 12:52:42 +01:00
|
|
|
private var endMessagesMissing = numSenders
|
|
|
|
|
private var correspondingSender: ActorRef = null // the Actor which send the Start message will also receive the report
|
2016-04-19 17:38:26 +02:00
|
|
|
|
|
|
|
|
def receive = {
|
2016-06-05 19:11:52 +02:00
|
|
|
case msg: Array[Byte] ⇒
|
|
|
|
|
if (msg.length != payloadSize) throw new IllegalArgumentException("Invalid message")
|
2016-12-30 12:52:42 +01:00
|
|
|
|
|
|
|
|
report()
|
2016-08-30 14:37:11 +02:00
|
|
|
case msg: TestMessage ⇒
|
2016-12-30 12:52:42 +01:00
|
|
|
report()
|
|
|
|
|
|
|
|
|
|
case Start(corresponding) ⇒
|
|
|
|
|
if (corresponding == self) correspondingSender = sender()
|
2016-04-19 17:38:26 +02:00
|
|
|
sender() ! Start
|
2016-12-30 12:52:42 +01:00
|
|
|
|
|
|
|
|
case End if endMessagesMissing > 1 ⇒
|
|
|
|
|
endMessagesMissing -= 1 // wait for End message from all senders
|
|
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
case End ⇒
|
2016-06-15 15:12:33 +02:00
|
|
|
if (printTaskRunnerMetrics)
|
|
|
|
|
taskRunnerMetrics.printHistograms()
|
2016-12-30 12:52:42 +01:00
|
|
|
correspondingSender ! EndResult(c)
|
2016-04-19 17:38:26 +02:00
|
|
|
context.stop(self)
|
2016-12-30 12:52:42 +01:00
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
case m: Echo ⇒
|
|
|
|
|
sender() ! m
|
2016-12-30 12:52:42 +01:00
|
|
|
}
|
2016-06-05 19:11:52 +02:00
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
def report(): Unit = {
|
|
|
|
|
reporter.onMessage(1, payloadSize)
|
|
|
|
|
c += 1
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
def senderProps(mainTarget: ActorRef, targets: Array[ActorRef], testSettings: TestSettings, plotRef: ActorRef,
|
2016-12-30 12:50:24 +01:00
|
|
|
printTaskRunnerMetrics: Boolean, reporter: BenchmarkFileReporter): Props =
|
2016-12-30 12:52:42 +01:00
|
|
|
Props(new Sender(mainTarget, targets, testSettings, plotRef, printTaskRunnerMetrics, reporter))
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
class Sender(target: ActorRef, targets: Array[ActorRef], testSettings: TestSettings, plotRef: ActorRef, printTaskRunnerMetrics: Boolean, reporter: BenchmarkFileReporter)
|
2016-06-15 15:12:33 +02:00
|
|
|
extends Actor {
|
2016-12-30 12:52:42 +01:00
|
|
|
val numTargets = targets.size
|
|
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
import testSettings._
|
|
|
|
|
val payload = ("0" * testSettings.payloadSize).getBytes("utf-8")
|
|
|
|
|
var startTime = 0L
|
|
|
|
|
var remaining = totalMessages
|
|
|
|
|
var maxRoundTripMillis = 0L
|
2016-06-15 15:12:33 +02:00
|
|
|
val taskRunnerMetrics = new TaskRunnerMetrics(context.system)
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-07-04 15:59:44 +02:00
|
|
|
context.system.eventStream.subscribe(self, classOf[ReceivedActorRefCompressionTable])
|
|
|
|
|
|
|
|
|
|
val compressionEnabled =
|
|
|
|
|
RARP(context.system).provider.transport.isInstanceOf[ArteryTransport] &&
|
2016-09-01 09:07:39 +03:00
|
|
|
RARP(context.system).provider.remoteSettings.Artery.Enabled
|
2016-07-04 15:59:44 +02:00
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
def receive = {
|
|
|
|
|
case Run ⇒
|
2016-07-04 15:59:44 +02:00
|
|
|
if (compressionEnabled) {
|
2016-12-30 12:52:42 +01:00
|
|
|
target ! Warmup(payload)
|
2016-07-04 15:59:44 +02:00
|
|
|
context.setReceiveTimeout(1.second)
|
|
|
|
|
context.become(waitingForCompression)
|
2016-12-30 12:52:42 +01:00
|
|
|
} else runWarmup()
|
2016-07-04 15:59:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def waitingForCompression: Receive = {
|
|
|
|
|
case ReceivedActorRefCompressionTable(_, table) ⇒
|
fix many bugs in InboundCompressions, #21464
* comprehensive integration test that revealed many bugs
* confirmations of manifests were wrong, at two places
* using wrong tables when system is restarted, including
originUid in the tables with checks when receiving advertisments
* close (stop scheduling) of advertisments when new incarnation,
quarantine, or restart
* cleanup how deadLetters ref was treated, and made it more robust
* make Decoder tolerant to decompression failures, can happen in
case of system restart before handshake completed
* give up resending advertisment after a few attempts without confirmation,
to avoid keeping outbound association open to possible dead system
* don't advertise new table when no inbound messages,
to avoid keeping outbound association open to possible dead system
* HeaderBuilder could use manifest field from previous message, added
resetMessageFields
* No compression for ArteryMessage, e.g. handshake messages must go
through without depending on compression tables being in sync
* improve debug logging, including originUid
2016-09-15 11:27:00 +02:00
|
|
|
if (table.dictionary.contains(target)) {
|
2016-07-04 15:59:44 +02:00
|
|
|
context.setReceiveTimeout(Duration.Undefined)
|
2016-12-30 12:52:42 +01:00
|
|
|
runWarmup()
|
2016-07-04 15:59:44 +02:00
|
|
|
} else
|
2016-12-30 12:52:42 +01:00
|
|
|
target ! Warmup(payload)
|
2016-07-04 15:59:44 +02:00
|
|
|
case ReceiveTimeout ⇒
|
2016-12-30 12:52:42 +01:00
|
|
|
target ! Warmup(payload)
|
2016-07-04 15:59:44 +02:00
|
|
|
}
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
def runWarmup(): Unit = {
|
|
|
|
|
sendBatch(warmup = true) // first some warmup
|
|
|
|
|
targets.foreach(_ ! Start(target)) // then Start, which will echo back here
|
|
|
|
|
context.become(warmup)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def warmup: Receive = {
|
2016-04-19 17:38:26 +02:00
|
|
|
case Start ⇒
|
|
|
|
|
println(s"${self.path.name}: Starting benchmark of $totalMessages messages with burst size " +
|
|
|
|
|
s"$burstSize and payload size $payloadSize")
|
|
|
|
|
startTime = System.nanoTime
|
|
|
|
|
remaining = totalMessages
|
2016-12-30 12:52:42 +01:00
|
|
|
(0 until sent.size).foreach(i ⇒ sent(i) = 0)
|
2016-04-19 17:38:26 +02:00
|
|
|
// have a few batches in flight to make sure there are always messages to send
|
|
|
|
|
(1 to 3).foreach { _ ⇒
|
|
|
|
|
val t0 = System.nanoTime()
|
2016-12-30 12:52:42 +01:00
|
|
|
sendBatch(warmup = false)
|
2016-04-19 17:38:26 +02:00
|
|
|
sendFlowControl(t0)
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
context.become(active)
|
|
|
|
|
|
|
|
|
|
case _: Warmup ⇒
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def active: Receive = {
|
2016-04-19 17:38:26 +02:00
|
|
|
case c @ FlowControl(t0) ⇒
|
|
|
|
|
val now = System.nanoTime()
|
|
|
|
|
val duration = NANOSECONDS.toMillis(now - t0)
|
|
|
|
|
maxRoundTripMillis = math.max(maxRoundTripMillis, duration)
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
sendBatch(warmup = false)
|
2016-04-19 17:38:26 +02:00
|
|
|
sendFlowControl(now)
|
2016-12-30 12:52:42 +01:00
|
|
|
}
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
val waitingForEndResult: Receive = {
|
2016-04-19 17:38:26 +02:00
|
|
|
case EndResult(totalReceived) ⇒
|
|
|
|
|
val took = NANOSECONDS.toMillis(System.nanoTime - startTime)
|
2016-04-22 16:24:12 +02:00
|
|
|
val throughput = (totalReceived * 1000.0 / took)
|
2016-12-30 12:50:24 +01:00
|
|
|
|
|
|
|
|
reporter.reportResults(
|
2016-04-19 17:38:26 +02:00
|
|
|
s"=== MaxThroughput ${self.path.name}: " +
|
2016-06-05 19:11:52 +02:00
|
|
|
f"throughput ${throughput * testSettings.senderReceiverPairs}%,.0f msg/s, " +
|
2016-07-04 16:42:14 +02:00
|
|
|
f"${throughput * payloadSize * testSettings.senderReceiverPairs}%,.0f bytes/s (payload), " +
|
|
|
|
|
f"${throughput * totalSize(context.system) * testSettings.senderReceiverPairs}%,.0f bytes/s (total" +
|
2016-09-01 09:07:39 +03:00
|
|
|
(if (RARP(context.system).provider.remoteSettings.Artery.Advanced.Compression.Enabled) ",compression" else "") + "), " +
|
2016-04-19 17:38:26 +02:00
|
|
|
s"dropped ${totalMessages - totalReceived}, " +
|
|
|
|
|
s"max round-trip $maxRoundTripMillis ms, " +
|
|
|
|
|
s"burst size $burstSize, " +
|
|
|
|
|
s"payload size $payloadSize, " +
|
2016-07-04 16:42:14 +02:00
|
|
|
s"total size ${totalSize(context.system)}, " +
|
2016-12-30 12:52:42 +01:00
|
|
|
s"$took ms to deliver $totalReceived messages.")
|
2016-06-15 15:12:33 +02:00
|
|
|
|
|
|
|
|
if (printTaskRunnerMetrics)
|
|
|
|
|
taskRunnerMetrics.printHistograms()
|
|
|
|
|
|
2016-06-05 19:11:52 +02:00
|
|
|
plotRef ! PlotResult().add(testName, throughput * payloadSize * testSettings.senderReceiverPairs / 1024 / 1024)
|
2016-04-19 17:38:26 +02:00
|
|
|
context.stop(self)
|
2016-07-04 15:59:44 +02:00
|
|
|
|
|
|
|
|
case c: ReceivedActorRefCompressionTable ⇒
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
val sent = new Array[Long](targets.size)
|
|
|
|
|
def sendBatch(warmup: Boolean): Unit = {
|
2016-04-19 17:38:26 +02:00
|
|
|
val batchSize = math.min(remaining, burstSize)
|
|
|
|
|
var i = 0
|
|
|
|
|
while (i < batchSize) {
|
2016-12-30 12:52:42 +01:00
|
|
|
val msg0 =
|
2016-08-30 14:37:11 +02:00
|
|
|
if (realMessage)
|
|
|
|
|
TestMessage(
|
|
|
|
|
id = totalMessages - remaining + i,
|
|
|
|
|
name = "abc",
|
|
|
|
|
status = i % 2 == 0,
|
|
|
|
|
description = "ABC",
|
|
|
|
|
payload = payload,
|
|
|
|
|
items = Vector(TestMessage.Item(1, "A"), TestMessage.Item(2, "B")))
|
|
|
|
|
else payload
|
|
|
|
|
|
2016-12-30 12:52:42 +01:00
|
|
|
val msg1 = if (warmup) Warmup(msg0) else msg0
|
|
|
|
|
|
|
|
|
|
targets(i % numTargets).tell(msg1, ActorRef.noSender)
|
|
|
|
|
sent(i % numTargets) += 1
|
2016-04-19 17:38:26 +02:00
|
|
|
i += 1
|
|
|
|
|
}
|
|
|
|
|
remaining -= batchSize
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def sendFlowControl(t0: Long): Unit = {
|
2016-12-30 12:52:42 +01:00
|
|
|
if (remaining <= 0) {
|
|
|
|
|
context.become(waitingForEndResult)
|
|
|
|
|
targets.foreach(_ ! End)
|
|
|
|
|
} else
|
2016-04-19 17:38:26 +02:00
|
|
|
target ! FlowControl(t0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final case class TestSettings(
|
2016-07-07 10:27:24 +02:00
|
|
|
testName: String,
|
|
|
|
|
totalMessages: Long,
|
|
|
|
|
burstSize: Int,
|
|
|
|
|
payloadSize: Int,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs: Int,
|
|
|
|
|
realMessage: Boolean) {
|
2016-07-04 16:42:14 +02:00
|
|
|
// data based on measurement
|
2016-09-01 09:07:39 +03:00
|
|
|
def totalSize(system: ActorSystem) = payloadSize + (if (RARP(system).provider.remoteSettings.Artery.Advanced.Compression.Enabled) 38 else 110)
|
2016-07-04 16:42:14 +02:00
|
|
|
}
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-06-05 19:11:52 +02:00
|
|
|
class TestSerializer(val system: ExtendedActorSystem) extends SerializerWithStringManifest with ByteBufferSerializer {
|
|
|
|
|
|
|
|
|
|
val FlowControlManifest = "A"
|
|
|
|
|
|
|
|
|
|
override val identifier: Int = 100
|
|
|
|
|
|
|
|
|
|
override def manifest(o: AnyRef): String =
|
|
|
|
|
o match {
|
|
|
|
|
case _: FlowControl ⇒ FlowControlManifest
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def toBinary(o: AnyRef, buf: ByteBuffer): Unit =
|
|
|
|
|
o match {
|
|
|
|
|
case FlowControl(burstStartTime) ⇒ buf.putLong(burstStartTime)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def fromBinary(buf: ByteBuffer, manifest: String): AnyRef =
|
|
|
|
|
manifest match {
|
|
|
|
|
case FlowControlManifest ⇒ FlowControl(buf.getLong)
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-15 15:12:33 +02:00
|
|
|
override def toBinary(o: AnyRef): Array[Byte] = o match {
|
|
|
|
|
case FlowControl(burstStartTime) ⇒
|
|
|
|
|
val buf = ByteBuffer.allocate(8)
|
|
|
|
|
toBinary(o, buf)
|
|
|
|
|
buf.flip()
|
|
|
|
|
val bytes = Array.ofDim[Byte](buf.remaining)
|
|
|
|
|
buf.get(bytes)
|
|
|
|
|
bytes
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def fromBinary(bytes: Array[Byte], manifest: String): AnyRef =
|
|
|
|
|
fromBinary(ByteBuffer.wrap(bytes), manifest)
|
2016-06-05 19:11:52 +02:00
|
|
|
}
|
|
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MaxThroughputSpecMultiJvmNode1 extends MaxThroughputSpec
|
|
|
|
|
class MaxThroughputSpecMultiJvmNode2 extends MaxThroughputSpec
|
|
|
|
|
|
2016-09-19 13:22:54 +02:00
|
|
|
abstract class MaxThroughputSpec extends RemotingMultiNodeSpec(MaxThroughputSpec) with PerfFlamesSupport {
|
2016-04-19 17:38:26 +02:00
|
|
|
|
|
|
|
|
import MaxThroughputSpec._
|
|
|
|
|
|
|
|
|
|
val totalMessagesFactor = system.settings.config.getDouble("akka.test.MaxThroughputSpec.totalMessagesFactor")
|
2016-08-30 14:37:11 +02:00
|
|
|
val realMessage = system.settings.config.getBoolean("akka.test.MaxThroughputSpec.real-message")
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-04-22 16:24:12 +02:00
|
|
|
var plot = PlotResult()
|
|
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
def adjustedTotalMessages(n: Long): Long = (n * totalMessagesFactor).toLong
|
|
|
|
|
|
|
|
|
|
override def initialParticipants = roles.size
|
|
|
|
|
|
|
|
|
|
def remoteSettings = system.asInstanceOf[ExtendedActorSystem].provider.asInstanceOf[RemoteActorRefProvider].remoteSettings
|
|
|
|
|
|
|
|
|
|
lazy val reporterExecutor = Executors.newFixedThreadPool(1)
|
2016-05-03 21:16:30 +02:00
|
|
|
def reporter(name: String): TestRateReporter = {
|
|
|
|
|
val r = new TestRateReporter(name)
|
2016-04-19 17:38:26 +02:00
|
|
|
reporterExecutor.execute(r)
|
|
|
|
|
r
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def afterAll(): Unit = {
|
|
|
|
|
reporterExecutor.shutdown()
|
2016-04-22 16:24:12 +02:00
|
|
|
runOn(first) {
|
|
|
|
|
println(plot.csv(system.name))
|
|
|
|
|
}
|
2016-04-19 17:38:26 +02:00
|
|
|
super.afterAll()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def identifyReceiver(name: String, r: RoleName = second): ActorRef = {
|
|
|
|
|
system.actorSelection(node(r) / "user" / name) ! Identify(None)
|
2016-12-30 12:52:42 +01:00
|
|
|
expectMsgType[ActorIdentity](10.seconds).ref.get
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val scenarios = List(
|
2016-06-05 19:11:52 +02:00
|
|
|
TestSettings(
|
|
|
|
|
testName = "warmup",
|
|
|
|
|
totalMessages = adjustedTotalMessages(20000),
|
|
|
|
|
burstSize = 1000,
|
|
|
|
|
payloadSize = 100,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs = 1,
|
|
|
|
|
realMessage),
|
2016-04-19 17:38:26 +02:00
|
|
|
TestSettings(
|
|
|
|
|
testName = "1-to-1",
|
2016-07-07 10:27:24 +02:00
|
|
|
totalMessages = adjustedTotalMessages(50000),
|
2016-04-19 17:38:26 +02:00
|
|
|
burstSize = 1000,
|
|
|
|
|
payloadSize = 100,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs = 1,
|
|
|
|
|
realMessage),
|
2016-04-19 17:38:26 +02:00
|
|
|
TestSettings(
|
|
|
|
|
testName = "1-to-1-size-1k",
|
|
|
|
|
totalMessages = adjustedTotalMessages(20000),
|
|
|
|
|
burstSize = 1000,
|
|
|
|
|
payloadSize = 1000,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs = 1,
|
|
|
|
|
realMessage),
|
2016-04-19 17:38:26 +02:00
|
|
|
TestSettings(
|
|
|
|
|
testName = "1-to-1-size-10k",
|
|
|
|
|
totalMessages = adjustedTotalMessages(10000),
|
|
|
|
|
burstSize = 1000,
|
|
|
|
|
payloadSize = 10000,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs = 1,
|
|
|
|
|
realMessage),
|
2016-04-19 17:38:26 +02:00
|
|
|
TestSettings(
|
|
|
|
|
testName = "5-to-5",
|
|
|
|
|
totalMessages = adjustedTotalMessages(20000),
|
2016-06-05 19:11:52 +02:00
|
|
|
burstSize = 200, // don't exceed the send queue capacity 200*5*3=3000
|
2016-04-19 17:38:26 +02:00
|
|
|
payloadSize = 100,
|
2016-08-30 14:37:11 +02:00
|
|
|
senderReceiverPairs = 5,
|
|
|
|
|
realMessage))
|
2016-04-19 17:38:26 +02:00
|
|
|
|
2016-12-30 12:50:24 +01:00
|
|
|
def test(testSettings: TestSettings, resultReporter: BenchmarkFileReporter): Unit = {
|
2016-04-19 17:38:26 +02:00
|
|
|
import testSettings._
|
|
|
|
|
val receiverName = testName + "-rcv"
|
|
|
|
|
|
2016-06-24 11:21:21 +02:00
|
|
|
runPerfFlames(first, second)(delay = 5.seconds, time = 15.seconds)
|
|
|
|
|
|
2016-04-19 17:38:26 +02:00
|
|
|
runOn(second) {
|
|
|
|
|
val rep = reporter(testName)
|
|
|
|
|
for (n ← 1 to senderReceiverPairs) {
|
2016-06-15 15:12:33 +02:00
|
|
|
val receiver = system.actorOf(
|
2016-12-30 12:52:42 +01:00
|
|
|
receiverProps(rep, payloadSize, printTaskRunnerMetrics = n == 1, senderReceiverPairs),
|
2016-06-15 15:12:33 +02:00
|
|
|
receiverName + n)
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
enterBarrier(receiverName + "-started")
|
|
|
|
|
enterBarrier(testName + "-done")
|
|
|
|
|
rep.halt()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
runOn(first) {
|
|
|
|
|
enterBarrier(receiverName + "-started")
|
2016-04-22 16:24:12 +02:00
|
|
|
val ignore = TestProbe()
|
2016-12-30 12:52:42 +01:00
|
|
|
val receivers = (for (n ← 1 to senderReceiverPairs) yield identifyReceiver(receiverName + n)).toArray
|
2016-04-19 17:38:26 +02:00
|
|
|
val senders = for (n ← 1 to senderReceiverPairs) yield {
|
2016-12-30 12:52:42 +01:00
|
|
|
val receiver = receivers(n - 1)
|
2016-04-22 16:24:12 +02:00
|
|
|
val plotProbe = TestProbe()
|
2016-06-03 11:59:00 +02:00
|
|
|
val snd = system.actorOf(
|
2016-12-30 12:52:42 +01:00
|
|
|
senderProps(receiver, receivers, testSettings, plotProbe.ref, printTaskRunnerMetrics = n == 1, resultReporter),
|
2016-04-22 16:24:12 +02:00
|
|
|
testName + "-snd" + n)
|
|
|
|
|
val terminationProbe = TestProbe()
|
|
|
|
|
terminationProbe.watch(snd)
|
2016-04-19 17:38:26 +02:00
|
|
|
snd ! Run
|
2016-04-22 16:24:12 +02:00
|
|
|
(snd, terminationProbe, plotProbe)
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
senders.foreach {
|
2016-04-22 16:24:12 +02:00
|
|
|
case (snd, terminationProbe, plotProbe) ⇒
|
|
|
|
|
if (snd == senders.head._1) {
|
|
|
|
|
terminationProbe.expectTerminated(snd, barrierTimeout)
|
|
|
|
|
val plotResult = plotProbe.expectMsgType[PlotResult]
|
|
|
|
|
plot = plot.addAll(plotResult)
|
|
|
|
|
} else
|
|
|
|
|
terminationProbe.expectTerminated(snd, 10.seconds)
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
enterBarrier(testName + "-done")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enterBarrier("after-" + testName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"Max throughput of Artery" must {
|
2016-12-30 12:50:24 +01:00
|
|
|
val reporter = BenchmarkFileReporter("MaxThroughputSpec", system)
|
2016-04-19 17:38:26 +02:00
|
|
|
for (s ← scenarios) {
|
2016-12-30 12:50:24 +01:00
|
|
|
s"be great for ${s.testName}, burstSize = ${s.burstSize}, payloadSize = ${s.payloadSize}" in test(s, reporter)
|
2016-04-19 17:38:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|