From 1b1ef752fea16db4d10f2f8a4b6fae4cf7781a6d Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 21 Oct 2015 16:11:26 +0200 Subject: [PATCH] =act #16522 Optimize Stash creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * cache the stash capacity config value BEFORE [info] Result "testCustom": [info] 27.607 ±(99.9%) 7.924 us/op [Average] [info] (min, avg, max) = (16.928, 27.607, 760217.600), stdev = 1369.808 [info] CI (99.9%): [19.682, 35.531] (assumes normal distribution) [info] Samples, N = 323534 [info] mean = 27.607 ±(99.9%) 7.924 us/op [info] min = 16.928 us/op [info] p( 0.0000) = 16.928 us/op [info] p(50.0000) = 21.824 us/op [info] p(90.0000) = 31.680 us/op [info] p(95.0000) = 38.592 us/op [info] p(99.0000) = 57.920 us/op [info] p(99.9000) = 102.144 us/op [info] p(99.9900) = 371.471 us/op [info] p(99.9990) = 85461.364 us/op [info] p(99.9999) = 760217.600 us/op [info] max = 760217.600 us/op [info] Result "testDefault": [info] 26.465 ±(99.9%) 15.980 us/op [Average] [info] (min, avg, max) = (14.608, 26.465, 1461714.944), stdev = 2900.094 [info] CI (99.9%): [10.485, 42.444] (assumes normal distribution) [info] Samples, N = 356626 [info] mean = 26.465 ±(99.9%) 15.980 us/op [info] min = 14.608 us/op [info] p( 0.0000) = 14.608 us/op [info] p(50.0000) = 18.304 us/op [info] p(90.0000) = 22.816 us/op [info] p(95.0000) = 26.976 us/op [info] p(99.0000) = 41.647 us/op [info] p(99.9000) = 71.808 us/op [info] p(99.9900) = 139.696 us/op [info] p(99.9990) = 33048.112 us/op [info] p(99.9999) = 1461714.944 us/op [info] max = 1461714.944 us/op [info] Benchmark Mode Cnt Score Error Units [info] StashCreationBenchmark.testCustom sample 323534 27.607 ± 7.924 us/op [info] StashCreationBenchmark.testDefault sample 356626 26.465 ± 15.980 us/op AFTER: [info] Result "testCustom": [info] 22.710 ±(99.9%) 1.384 us/op [Average] [info] (min, avg, max) = (15.168, 22.710, 110886.912), stdev = 233.744 [info] CI (99.9%): [21.326, 24.094] (assumes normal distribution) [info] Samples, N = 308896 [info] mean = 22.710 ±(99.9%) 1.384 us/op [info] min = 15.168 us/op [info] p( 0.0000) = 15.168 us/op [info] p(50.0000) = 19.776 us/op [info] p(90.0000) = 28.608 us/op [info] p(95.0000) = 33.344 us/op [info] p(99.0000) = 49.216 us/op [info] p(99.9000) = 90.253 us/op [info] p(99.9900) = 594.334 us/op [info] p(99.9990) = 8350.329 us/op [info] p(99.9999) = 110886.912 us/op [info] max = 110886.912 us/op [info] Result "testDefault": [info] 19.099 ±(99.9%) 0.424 us/op [Average] [info] (min, avg, max) = (13.120, 19.099, 45940.736), stdev = 77.524 [info] CI (99.9%): [18.675, 19.523] (assumes normal distribution) [info] Samples, N = 361741 [info] mean = 19.099 ±(99.9%) 0.424 us/op [info] min = 13.120 us/op [info] p( 0.0000) = 13.120 us/op [info] p(50.0000) = 16.368 us/op [info] p(90.0000) = 24.250 us/op [info] p(95.0000) = 31.232 us/op [info] p(99.0000) = 55.680 us/op [info] p(99.9000) = 121.249 us/op [info] p(99.9900) = 402.386 us/op [info] p(99.9990) = 1767.264 us/op [info] p(99.9999) = 45940.736 us/op [info] max = 45940.736 us/op [info] [info] [info] # Run complete. Total time: 00:02:01 [info] [info] Benchmark Mode Cnt Score Error Units [info] StashCreationBenchmark.testCustom sample 308896 22.710 ± 1.384 us/op [info] StashCreationBenchmark.testDefault sample 361741 19.099 ± 0.424 us/op --- .../src/main/scala/akka/actor/Stash.scala | 10 +-- .../main/scala/akka/dispatch/Mailboxes.scala | 42 ++++++++++++- .../akka/actor/StashCreationBenchmark.scala | 61 +++++++++++++++++++ 3 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 akka-bench-jmh/src/main/scala/akka/actor/StashCreationBenchmark.scala diff --git a/akka-actor/src/main/scala/akka/actor/Stash.scala b/akka-actor/src/main/scala/akka/actor/Stash.scala index feaec7e64b..1abbb9f6fb 100644 --- a/akka-actor/src/main/scala/akka/actor/Stash.scala +++ b/akka-actor/src/main/scala/akka/actor/Stash.scala @@ -123,14 +123,8 @@ private[akka] trait StashSupport { /* The capacity of the stash. Configured in the actor's mailbox or dispatcher config. */ - private val capacity: Int = { - val dispatcher = context.system.settings.config.getConfig(context.props.dispatcher) - val fallback = dispatcher.withFallback(context.system.settings.config.getConfig(Mailboxes.DefaultMailboxId)) - val config = - if (context.props.mailbox == Mailboxes.DefaultMailboxId) fallback - else context.system.settings.config.getConfig(context.props.mailbox).withFallback(fallback) - config.getInt("stash-capacity") - } + private val capacity: Int = + context.system.mailboxes.stashCapacity(context.props.dispatcher, context.props.mailbox) /** * INTERNAL API. diff --git a/akka-actor/src/main/scala/akka/dispatch/Mailboxes.scala b/akka-actor/src/main/scala/akka/dispatch/Mailboxes.scala index 9f69bcd76a..59c70ee65c 100644 --- a/akka-actor/src/main/scala/akka/dispatch/Mailboxes.scala +++ b/akka-actor/src/main/scala/akka/dispatch/Mailboxes.scala @@ -6,7 +6,6 @@ package akka.dispatch import java.lang.reflect.ParameterizedType import java.util.concurrent.ConcurrentHashMap - import akka.ConfigurationException import akka.actor.{ Actor, ActorRef, ActorSystem, DeadLetter, Deploy, DynamicAccess, Props } import akka.dispatch.sysmsg.{ EarliestFirstSystemMessageList, LatestFirstSystemMessageList, SystemMessage, SystemMessageList } @@ -14,8 +13,9 @@ import akka.event.EventStream import akka.event.Logging.Warning import akka.util.Reflect import com.typesafe.config.{ Config, ConfigFactory } - import scala.util.control.NonFatal +import java.util.concurrent.atomic.AtomicReference +import scala.annotation.tailrec object Mailboxes { final val DefaultMailboxId = "akka.actor.default-mailbox" @@ -232,4 +232,42 @@ private[akka] class Mailboxes( .withFallback(settings.config.getConfig(id)) .withFallback(defaultMailboxConfig) } + + private val stashCapacityCache = new AtomicReference[Map[String, Int]](Map.empty[String, Int]) + private val defaultStashCapacity: Int = + stashCapacityFromConfig(Dispatchers.DefaultDispatcherId, Mailboxes.DefaultMailboxId) + + /** + * INTERNAL API: The capacity of the stash. Configured in the actor's mailbox or dispatcher config. + */ + private[akka] final def stashCapacity(dispatcher: String, mailbox: String): Int = { + + @tailrec def updateCache(cache: Map[String, Int], key: String, value: Int): Boolean = { + stashCapacityCache.compareAndSet(cache, cache.updated(key, value)) || + updateCache(stashCapacityCache.get, key, value) // recursive, try again + } + + if (dispatcher == Dispatchers.DefaultDispatcherId && mailbox == Mailboxes.DefaultMailboxId) + defaultStashCapacity + else { + val cache = stashCapacityCache.get + val key = dispatcher + "-" + mailbox + cache.get(key) match { + case Some(value) ⇒ value + case None ⇒ + val value = stashCapacityFromConfig(dispatcher, mailbox) + updateCache(cache, key, value) + value + } + } + } + + private def stashCapacityFromConfig(dispatcher: String, mailbox: String): Int = { + val disp = settings.config.getConfig(dispatcher) + val fallback = disp.withFallback(settings.config.getConfig(Mailboxes.DefaultMailboxId)) + val config = + if (mailbox == Mailboxes.DefaultMailboxId) fallback + else settings.config.getConfig(mailbox).withFallback(fallback) + config.getInt("stash-capacity") + } } diff --git a/akka-bench-jmh/src/main/scala/akka/actor/StashCreationBenchmark.scala b/akka-bench-jmh/src/main/scala/akka/actor/StashCreationBenchmark.scala new file mode 100644 index 0000000000..54baf8c547 --- /dev/null +++ b/akka-bench-jmh/src/main/scala/akka/actor/StashCreationBenchmark.scala @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2015 Typesafe Inc. + */ +package akka.actor + +import scala.concurrent.Await +import scala.concurrent.duration._ +import akka.testkit.TestProbe +import com.typesafe.config.ConfigFactory +import org.openjdk.jmh.annotations._ +import java.util.concurrent.TimeUnit + +object StashCreationBenchmark { + class StashingActor extends Actor with Stash { + def receive = { + case msg => sender() ! msg + } + } + + val props = Props[StashingActor] +} + +@State(Scope.Benchmark) +@BenchmarkMode(Array(Mode.SampleTime)) +@Fork(3) +@Warmup(iterations = 5) +@Measurement(iterations = 10) +class StashCreationBenchmark { + val conf = ConfigFactory.parseString(""" + my-dispatcher = { + stash-capacity = 1000 + } + """) + implicit val system: ActorSystem = ActorSystem("StashCreationBenchmark", conf) + val probe = TestProbe() + + @TearDown(Level.Trial) + def shutdown() { + system.terminate() + Await.ready(system.whenTerminated, 15.seconds) + } + + @Benchmark + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def testDefault: Boolean = { + val stash = system.actorOf(StashCreationBenchmark.props) + stash.tell("hello", probe.ref) + probe.expectMsg("hello") + true + } + + @Benchmark + @OutputTimeUnit(TimeUnit.MICROSECONDS) + def testCustom: Boolean = { + val stash = system.actorOf(StashCreationBenchmark.props.withDispatcher("my-dispatcher")) + stash.tell("hello", probe.ref) + probe.expectMsg("hello") + true + } +} +