-
-
Notifications
You must be signed in to change notification settings - Fork 10.1k
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
tap: memoize loading installed taps #16806
Conversation
specify "::names" do | ||
expect(described_class.names.sort).to eq(["homebrew/core", "homebrew/foo"]) | ||
end | ||
|
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 will be deprecated soonish so I figured we might as well remove the test instead of updating it. I guess we weren't automatically including the core cask tap on macOS before in this list.
e1d55c6
to
76d638f
Compare
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.
Seems fine to me. Feels a little weird to add a new concept ("tap registry") instead of just busting the original cache in these very unusual/never-in-a-hot-loop situations? Not a blocker though if you and @Bo98 both disagree.
Library/Homebrew/cmd/tap.rb
Outdated
@@ -60,7 +60,7 @@ def tap | |||
tap.fix_remote_configuration | |||
end | |||
elsif args.no_named? | |||
puts Tap.select(&:installed?) | |||
puts Tap.select(&:installed?).sort_by(&:to_s) |
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.
puts Tap.select(&:installed?).sort_by(&:to_s) | |
puts Tap.select(&:installed?).sort_by(&:name) |
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.
It should be equivalent but I'll change it back for clarity.
return to_enum unless block | ||
sig { returns(Integer) } | ||
def hash | ||
[self.class, name].hash |
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.
Do we actually need the class here? It shouldn't be possible to get a Tap
for e.g. homebrew/core
anyways.
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.
It is considered good practice to include, in case this is ever used in mixed-type collections: https://ruby-doc.org/3.2.2/Object.html#method-i-hash
def self.registry | ||
cache[:registry] ||= Set.new.tap do |set| | ||
if TAP_DIRECTORY.directory? | ||
set.merge TAP_DIRECTORY.subdirs |
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.
Doesn't merge
create a new Set
?
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.
No, it always returns self
:
Merges the elements of the given enumerable object to the set and returns self.
Yes, given this probably only happens rarely, maybe |
Maybe the simplest solution would be to call |
I'm not a fan of this unless we wind up putting more stuff in the class level |
Busting the class level Let me sketch out the alternative implementation from what I gather from the comments.
I guess the advantage of this approach is that it doesn't require a separate The main difficulty with caching is remembering when to invalidate the cache and both potential implementations have the same weaknesses there. |
76d638f
to
4aeb71b
Compare
This comment was marked as resolved.
This comment was marked as resolved.
The only thing I can see going wrong is reduced performance. |
I doubt there is a way, otherwise we end up with the instance cache depending on other taps, which makes even less sense. |
This adds a new `Tap.tap_registry` method that will load all installed and core taps only once instead of doing it every time on the fly. We only expect this list to change when a tap is installed or uninstalled in the lifetime of the program. The only places where that can happen are in the `Tap#install` and `Tap#uninstall` methods so we make sure to update the tap registry when those methods are called. This increase the performance of `Tap#reverse_tap_migrations_renames` since most of that time was spent in `Tap.each` loading the installed taps from the tap directory. On my local computer, the performance goes from 3+ seconds to around 0.2 when calling `Tap.each(&:to_s)` in a loop 6k times. 6k is around the number of formulae in the core tap so this is not unheard of. Other changes: - Override `Tap#eql?` and `Tap.hash` so we can store them uniquely in sets - Update tests to check that taps are getting added and removed from the registry when they get installed and uninstalled
5515248
to
7961069
Compare
I was thinking of it more in the context of speeding up |
We should probably make |
Closing because the alternative PR already got merged in: #16863 |
brew style
with your changes locally?brew typecheck
with your changes locally?brew tests
with your changes locally?See #16791 (comment)
This adds a new
Tap.tap_registry
method that will load all installed and core taps only once instead of doing it every time on the fly. We only expect this list to change when a tap is installed or uninstalled in the lifetime of the program. The only places where that can happen are in theTap#install
andTap#uninstall
methods so we make sure to update the tap registry when those methods are called.This increases the performance of
Tap#reverse_tap_migrations_renames
since most of that time was spent inTap.each
loading the installed taps from the tap directory.On my local computer, the performance goes from 3+ seconds to around 0.2 when calling
Tap.each(&:to_s)
in a loop 6k times. 6k is around the number of formulae in the core tap so this is not unheard of.Before:
After:
There might be additional improvements that can be made to
Tap.reverse_tap_migrations_renames
but there will be diminishing returns. We can consider doing that work in a separate PR.