Skip to content
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

optimization: avoid MonadErrorOps allocations #370

Closed
wants to merge 1 commit into from

Conversation

fwbrasil
Copy link

Problem

The MonadErrorOps implicit class isn't an AnyVal, which ends up producing object instantiation in certain scenarios where the JIT compiler isn't able to elide the allocation. This is likely not an oversight since AnyVals can't have by-name parameters, which the class uses.

Solution

Use an implicit conversion + AnyVal class instead to avoid allocation while still allowing the by-name parameter.

Notes

  • This change is part of Reduce allocation rate tapir#3552.
  • I wonder if the by-name input is really needed since this class is meant to be instantiated on each use and will immediately evaluate the parameter to call the MonadError implementation.
  • I'm not sure if the change is binary-compatible across the different Scala versions but I believe this is what implicit class desugars to.
  • The conversion from => F[A] to () => F[A] doesn't require an allocation, both have the same representation at runtime.

@adamw
Copy link
Member

adamw commented Feb 29, 2024

There's 2 mima failures, unfortunately:

[error] core: Failed binary compatibility check against com.softwaremill.sttp.shared:core_3:1.3.17! Found 2 potential problems
[error]  * static method MonadErrorOps(scala.Function0)sttp.monad.syntax#MonadErrorOps in class sttp.monad.syntax does not have a correspondent in current version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("sttp.monad.syntax.MonadErrorOps")
[error]  * method MonadErrorOps(scala.Function0)sttp.monad.syntax#MonadErrorOps in object sttp.monad.syntax does not have a correspondent in current version
[error]    filter with: ProblemFilters.exclude[DirectMissingMethodProblem]("sttp.monad.syntax.MonadErrorOps")

@adamw
Copy link
Member

adamw commented Feb 29, 2024

The by-name parameter I think is significant for handleError, which delegates to def handleError[T](rt: => F[T])(h: PartialFunction[Throwable, F[T]]): F[T] in MonadError - there could be exceptions when evaluating the r itself, which we want to catch.

@fwbrasil
Copy link
Author

oh, I thought it could desugar to the same bytecode but there's the class constructor. I'll close this PR for now. I'm planning to try Graal and it might be able to get rid of these allocations better than C2.

@fwbrasil fwbrasil closed this Feb 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants