-
-
Notifications
You must be signed in to change notification settings - Fork 354
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
Optimize SoftReference
caches using ConcurrentHashMap
's compute
#3641
base: main
Are you sure you want to change the base?
Conversation
9a459be
to
e045769
Compare
@@ -708,4 +685,36 @@ object ZincWorkerImpl { | |||
toAnalyze = List((List(start), deps(start).toList)) | |||
).reverse | |||
} | |||
|
|||
private class AutoCloseableCache[A, B <: AnyRef] extends Cache[A, B] { |
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.
Why is this called AutoCloseableCache
? Did you forget a with AutoCloseable
?
Also, I'd prefer to have Cache
defined before its derivatives.
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.
What I want to express is a cache that on clear
also tries to find AutoCloseable
values and close
them. I don't what is the best name for it. Do you have suggestions? I will change the order.
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.
Instead of a sub-class with a unlucky name we could add a handleAutoClosables: Boolean
parameter to clear
.
I'm personally unsure about the motivation here. Are we seeing performance bottlenecks that we think will be solved by this optimization? How will we know that this PR actually achieves what we hope it will? There's always a risk of bugs when making changes to this kind of concurrent code, both nondeterminsitic semantic bugs and performance bugs, and the risk typically goes up as your concurrency model gets more fine-grained. If we're not seeing any real bottlenecks, I would prefer to have coarse-grained synchronized blocks and locks over fine-grained concurrent data structures, the more coarse-grained the better |
860bb05
to
ac7e438
Compare
You are right, there is no particular performance reason behind this change. I should have named the PR
A possibly problematic behavioral change of this PR that I haven't noticed until now: |
I agree that there are probably tons of concurrency issues in the current implementation, but we still need to be clear about what we are trying to achieve here. Just dropping in To be clear, I think using |
case (_, v @ SoftReference(_)) => v | ||
case _ => SoftReference(create) | ||
} | ||
)() |
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 there a tiny risk that the instance returned by create
(or the existing one) is garbage-collected by the time the SoftReference
is dereferenced? Having a var to maintain a strong reference throughout the lifetime of getOrElseCreate
feels safer.
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.
Yeah, this doesn't look right. The scheme that I used in Soft
(which is a SoftReference
that can recreate itself if it's needed after it's cleared) is better: https://github.com/Ichoran/kse3/blob/5a900ad9578008f0f3454c759cb83e73ec0e21a8/flow/src/Cached.scala#L105-L154
.values() | ||
.iterator() | ||
.asScala | ||
.foreach { case SoftReference(v: AutoCloseable) => |
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.
foreach
doesn't take a partial function, so we should provide a catch-all case
. Not doing so will fail for non-Autoclosable
entries.
Opened a ticker to centralize discussion of the more general issue here #3730 |
@bjaglin raised a great point about concurrent initialization in mill-scalafix: joan38/mill-scalafix#206 (comment)
In
ZincWorkerImpl
were usingsynchronized
to avoid that, but the built-incompute
method injava.util.concurrent.ConcurrentHashMap
is probably better.Pull Request: #3641