=act #16522 Optimize Stash creation

* 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
This commit is contained in:
Patrik Nordwall 2015-10-21 16:11:26 +02:00
parent 18a24c0e50
commit 1b1ef752fe
3 changed files with 103 additions and 10 deletions

View file

@ -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.

View file

@ -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")
}
}

View file

@ -0,0 +1,61 @@
/**
* Copyright (C) 2015 Typesafe Inc. <http://www.typesafe.com>
*/
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
}
}