-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
=act Make Behavior.start tailrec. #28905
Conversation
// that deep, and if they are it's most likely a bug which will be seen as StackOverflowError | ||
if (maxDepth > 8192) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just hard code this for safety
Test PASSed. |
Test PASSed. |
Test PASSed. |
def internalStart( | ||
behavior: Behavior[T], | ||
ctx: TypedActorContext[T], | ||
interceptorsStack: List[InterceptorImpl[T, Any]] = Nil, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really a big improvement replacing the thread stack with our own, with another limit, is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can adjust the max depth and it's more consistent when error occur, isn't it?
Different environments have different Xss configs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't expect that we will ever see a stack overflow here even with small stacks, if the behavior stack grows so much that it does hit the max stack size it will be because of a bug, for example when each interpreted message adds a new interceptor somehow.
I think the comment that it should/could be tailrec was perhaps misleading
maxDepth: Int = 0): Behavior[T] = { | ||
// normal stack of interceptors and similar shouldn't be | ||
// that deep, and if they are it's most likely a bug which will be seen as StackOverflowError | ||
if (maxDepth > 8192) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This number can be adjusted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But how is 8192 chosen? And how would that be adjusted?
I don't think we should have a fixed magical number. We never know in which JVM this will run. And having it adjustable by the user seems to me a leaked abstraction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah ok, I should have search before commenting. 8192 is the deault StackOverflow size.
Not true what I wrote here. My question still then relevant. Why 8192? The issue is memory, not amount of nesting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@renatocaval For myself,I want to remove the limit of depth too, but as a PR, I think it would be great to simulate it's previous behavior.
I just pick up 8192 randomly because we do not know how many frames can be there before it throws a StackOverflowError
.
// note that this can't be @tailrec, but normal stack of interceptors and similar shouldn't be
// that deep, and if they are it's most likely a bug which will be seen as StackOverflowError
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I bring this up for discussion, thanks for your inputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated this and removed the maxDepth
as it can only be a bug when OOM.
akka-actor-typed/src/main/scala/akka/actor/typed/Behavior.scala
Outdated
Show resolved
Hide resolved
Test PASSed. |
} | ||
internalStart(behavior, ctx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this similar to the attempt in #27364 ?
It's replacing the stack with a collection. I'm not sure there are any advantages of doing that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have forgotten that. I came up with this once I read in detail of the Behavior code. With this change, it will work more concisely, at least it doesn't harm, isn't it? Different deploy environments XSS configs various too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran a temporary benchmark comparing the two and this is about 30% slower.
BEFORE:
[info] Benchmark (batchSize) (dispatcher) (mailbox) (tpt) Mode Cnt Score Error Units
[info] TypedActorBenchmark.start 50 fjp-dispatcher akka.dispatch.SingleConsumerOnlyUnboundedMailbox 50 thrpt 6 8048211,084 ± 156294,353 ops/s
AFTER:
[info] Benchmark (batchSize) (dispatcher) (mailbox) (tpt) Mode Cnt Score Error Units
[info] TypedActorBenchmark.start 50 fjp-dispatcher akka.dispatch.SingleConsumerOnlyUnboundedMailbox 50 thrpt 6 5772894,593 ± 257261,651 ops/s
Used the following in TypedActorBenchmark:
private val behv: Behavior[Start] =
Behaviors
.supervise[Start] {
Behaviors.setup[Start] { _ =>
Behaviors.withMdc(Map.empty[String, String]) {
Behaviors.receiveMessage[Start](_ => Behaviors.same)
}
}
}
.onFailure[Exception](SupervisorStrategy.restart)
def startSupervisor(iteration: Int): Behavior[Start] =
Behaviors.receive { (ctx, msg) =>
msg match {
case Start(respondTo) =>
val startNanoTime = System.nanoTime()
var i = 0
while (i < iteration) {
Behavior.start(behv, ctx)
i += 1
}
respondTo ! Completed(startNanoTime)
Behaviors.same
}
}
@Benchmark
@OperationsPerInvocation(iterations)
def start(): Unit = {
Await.result(system.ask(Start), timeout)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then it's not worth it.
Test PASSed. |
Closing this because I don't think it is an improvement over status quo. |
but thanks anyway |
The performance is always critical for Akka, let's keep this in mind once we do encounter any problem later. |
Make it a tailrec and using a depth limit to it.