-
Notifications
You must be signed in to change notification settings - Fork 788
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
asdf shims adding too much time to use them in a shell prompt #290
Comments
You are right, we need to investigate this.
Now I have over half a dozen plugins installed, but even running it for just one version number still causes it to take a quarter of a second. |
Reminder for myself: http://tldp.org/LDP/abs/html/optimizations.html |
Same here (macOS Ruby→ time ~/.asdf/shims/ruby -v
ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin16]
real 0m0.333s
user 0m0.200s
sys 0m0.081s
# --
→ time asdf current ruby
2.5.1 (set by $HOME/.tool-versions)
real 0m0.433s
user 0m0.269s
sys 0m0.130s Node→ time ~/.asdf/shims/node -v
v10.7.0
real 0m0.219s
user 0m0.132s
sys 0m0.065s
# --
→ time asdf current nodejs
10.7.0 (set by $HOME/.tool-versions)
real 0m0.501s
user 0m0.308s
sys 0m0.155s Python→ time ~/.asdf/shims/python --version
Python 3.7.0
real 0m0.197s
user 0m0.121s
sys 0m0.062s
# --
→ time asdf current python
3.7.0 (set by $HOME/.tool-versions)
real 0m0.476s
user 0m0.302s
sys 0m0.142s edit: included macOS version and timings with |
@ppdeassis so asdf current doesn't actually execute the version commands directly (e.g. |
Yeah, probably. But even then - achieving the direct Checkout
That's 0.019s ( But again, as @stevenocchipinti pointed out, I don't know if it's an |
@ppdeassis the only thing involved is the plugin's |
I believe it's due to the logic in asdf's version lookup code. It looks for one or more files in the current directory, and climbs up to the parent directory if nothing is found. This continues until a version is found or the top level directory is reached. |
Correct me if I'm wrong, but the lookup appears to traverse the directory hierarchy per plugin, so the overhead scales with the number of enabled plugins. Since those are making file read calls they're relatively expensive. It could be a significant improvement to traverse the directory tree once, compiling applicable versions from |
@brlodi yes, that is exactly why the |
Is there an option to disable shims entirely? They don't do anything relevant to me, I really just need updating a bunch of env variables when I "switch env", but they do slow down every command |
Could you people test with current master (cc78863) and report if shim execution has improved with the new shim exec? |
I've just cloned the current
Still seems to be slow. RubyInstalled with
Installed with
NodeJSInstalled with
Installed with
|
As a workaround, I was able to incorporate asdf into my prompt with no noticeable performance impact by implementing the lookup myself in bash. # Code goes somewhere in your prompt function.
asdf_path="$PWD"
asdf_info=
# Make sure asdf is currently active.
if [ "$ASDF_BIN" ]; then
# Traverse up directories until we hit an asdf directory or filesystem root.
until [ -f "$asdf_path/.tool-versions" -o "$asdf_path" == '' ]; do
asdf_path="${asdf_path%/*}"
done
asdf_path="$asdf_path/.tool-versions"
# Use array manipulations and IFS to replace newlines with '/'.
if [ -f "$asdf_path" ]; then
IFS=$'\n'
asdf_info=($(<"$asdf_path"))
IFS='/'
asdf_info="${asdf_info[*]}"
fi
fi An example of what |
@metakirby5 This is mostly how it was implemented a while ago, but as asdf started handling other things, the logic got more complex, making things slower. |
Just a random thought, if that legacy logic is where the bottleneck is, would it be worth disabling that behaviour by default and legacy users can enable |
We already have a setting for this in asdf configuration file: https://asdf-vm.com/#/core-configuration?id=homeasdfrc I am currently doing a small experiment on my side with the lookup logic implemented in Go. |
@danhper that's fantastic. 👍 Please keep going with that. Speed-ups for me: Normal asdf$ time ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin17]
real 0m0.262s user 0m0.137s sys 0m0.158s With asdf-exec$ time ruby -v
ruby 2.6.2p47 (2019-03-13 revision 67232) [x86_64-darwin17]
real 0m0.041s user 0m0.015s sys 0m0.014s Direct path to binary
|
Sorry for the delay, guys. My findings won't probably add anything new, but...
|
So, I just created a new plugin to integrate with direnv which can hopefully improve execution time a lot. It works by exposing your asdf-tools environment (PATH and any other env-var set by your active plugins) and let direnv manage it. Be sure to checkout the README for https://github.com/asdf-community/asdf-direnv |
Any update on this? It is still incredibly slow for me to the point that I just disabled asdf. It seems like the entire parent lookup could be done in one command like this (48ms on a low-end macbook pro)
Then lookups for any plugin (legacy or not) can just check against that string rather than performing 25 separate calls. I don't have any local versions and I very rarely need to switch versions, but asdf is slowing down my terminal every time I open it. I'd also be fine if asdf delayed the lookup until a command is used. e.g. don't look for |
Any news on this issue? It seems like there could be some solutions, if given enough priority. |
I encountered the same issue. With shims:
I replaced ln -s ../installs/lua/5.4.0/bin/lua lua
Maybe using symlinks instead of wrapper scripts would do the job? |
@danhper any news on keeping asdf-exec up to date? It seems fantastically fast!! |
Over half a second is too much. Node version is part of my prompt (via starship.rs) and it's extremely slow. |
Seems like this tool is trying to sovle the issue. |
Even though asdf looks to be a very useful tool, it unfortunately has a fatal flaw related performance. Each invocation of any asdf-managed tool incurs at 300-400ms delay. I know that probably doesn't sound like much, but some scripts that make dozens of invocations (to perl, for example) easily take upwards of 20-30 seconds to load when using asdf (and about 10ms without it). There's a direnv plugin that tries to solve the problem (and actually performs quite well), but requires littering `.envrc` files throughout the file system (in addition to the `.tool-versions` and other files that asdf already requires). And seeing as I was using asdf in an effort to consolidate tools and reduce clutter, this is a no-go. I'm very sad to have to say goodbye, but hopefully someday they will fix this extremely detrimental performance problem (and I'll be first back in line) See: asdf-vm/asdf#290
Just hit this problem today trying to make a prompt that shows the current version of various tools. It's not only that In my opinion, this is a major drawback against using There seems to have been some research earlier in this issue that hinted at possible improvements. Is there anything we can do to fix this problem in |
Correct me if I'm wrong, but the setting For reference, this Fish shell function currently implements function asdf_current --argument-names lang
set current (pwd)
set versions
set root (dirname $HOME)
env_lang_version=ASDF_(string upper $lang)_VERSION set env_version $$env_lang_version
if test -n "$env_version"
echo $env_version
return 0
end
while test "$current" != "$root"
if test -e $current/.tool-versions
set -a versions (string split "\n" < $current/.tool-versions)
end
set current (string join "/" (string split "/" $current)[..-2])
end
for ver in $versions
if string match --quiet "$lang *" $ver
echo (string split -f2 " " "$ver")
return 0
end
end
end This obviously isn't usable in |
Many thanks @blopker. Ruby is working as intended for now. If/when I get around to it, I'll make a separate issue for my personal problems with proper documentation. With the most polite of intentions. I was purely curious if the status quo re: Bash as the long term architecture, had changed since April. But I don't mean to be rude or impose my opinions on the maintainers. I don't know the ins and outs of the project, and you have to be respectful of the maintainer's commitment/other goals. Adding "make an asdf patch in Rust/other programming language" to my backlog of "cool projects I might do one day" without fitting in with what they want seemed rude. |
No worries, I was only curious if you found a use case that was taking longer than 100ms to complete. I don't think you were rude at all. Performance is a tricky thing to get right though, so having a way to reproduce the issue helps a lot. |
What are the conditions under which people would consider this issue closed? Can someone do a stocktake of the performance improvements in this thread that have and have not been implemented? Personally, I think minimum a criteria would include:
|
The majority of the time spent in most asdf command is in wait4, from all of the forked processes, subshells, and pipes.
While a lot of it is unavoidable, there is room for improvement. I started with the commands in use for
I haven't thoroughly tested these changes with bats (hence not opening a PR), but casual usage indicates it's the same. Anyway, I think there's probably room to grab more milliseconds here and there. EDIT:
|
Thanks for the investigation @stephanGarland ! I guess I need to invest in learning |
Agreed; I've opened a Draft PR here to capture everything, and @jthegedus mentioned using hyperfine to benchmark changes.
No, but I think for a tool like asdf it's the first priority. I want shell commands to execute at the speed of thought. Adding 50-100 ms is noticeable, and for me, it was enough to stop using asdf (until I started poking around, which has been fun).
I mean, the same is true as re-writing into another language. I'd also argue that some of the previous shell included in asdf, like the
Personally I hate the entire Go ecosystem, but you do you 😛 Given that the overwhelming the majority of the calls are due to waiting on child processes to return, re-writing in any language where everything is inside a single process would have performance benefits. |
I'm just a number, but for what is worth, I use chruby and chnode due to the incredible performance and simpler environment setup (there aren't many proxies), I had to stop using asdf due to the performance penalties. For Go, I just install the last version due to backward compatibility. I haven't found an alternative to asdf for Elixir, but I'm not using the language nowdays. |
Same here, fwiw. I would prefer asdf but switched back to rbenv and nvm because of the startup lag. |
I also found asdf's performance to be unusable. It made my shell prompt take seconds longer to start with just a few plugins. That's every time I ran a shell command. I wrote an alternative to asdf. It's written in rust so it's extremely fast, but more importantly it doesn't use shims at all so launching runtimes is exactly as fast as calling them directly—because you are. It still uses asdf plugins which is definitely the best part of asdf. It also has a few more features that I really wanted out of asdf (fuzzy-matching and aliases to name a couple). Let me know what you think. |
That sounds amazing, I was looking for a non-shims solution that would achieve this type of performance. I'll give it a review tomorrow! |
@jdxcode great to hear! I'm giving it a spin. So far so good! time ruby --version
ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c5) [arm64-darwin22]
real 0m0.019s 🏎️ 🚀 |
@pedropombeiro it's worth noting that the performance of Since rtx doesn't use shims, if you call Of course if rtx did use shims the overhead is so small it wouldn't matter. Performance isn't the only reason I wanted to avoid shims, I also didn't want to break So in asdf when you might want to set |
For those not reading the entire comment thread, there is a plugin for Direnv (https://github.com/asdf-community/asdf-direnv) which modifies asdf to avoid the "Shims model". Thanks for sharing @jdxcode |
I wasn't happy with |
I'm a regular workflow I imagine one runs the commands orders of magnitude more times than changing into the project folder... |
Seeing as this thread is form 2018, the chance that |
I ran into this issue this week when using asdf in a test runner. The test runner is composed of multiple tools that call each other (like clojure and java). Each call incurs the asdf penalty of 450ms, and because of multiple nested calls, this adds up. One fix I discovered was to put the binaries explicitly on the PATH
which shows a dramatic improvement in cycle time (from 8s to 2s per test run, for me). I will investigate alternative solutions (asdf-direnv, rtx) as well. |
(This may be a good occasion to say thanks for asdf - a wonderful tool that has made working in a team with consistent tool versions a breeze, and boosted productivity in my company) |
Awhile back, someone ported ASDF to Rust and made the whole experience lightning-fast. It uses all the same asdf plugins, but if you find ASDF sluggish, try mise. |
Hello,
Not sure if this is the right place to report this issue but I've noticed using
asdf
is quite slow for executing commands.This is
ruby
usingchruby
:and
node
usingnvm
:Steps to reproduce
This is
ruby
usingasdf
:and
node
usingasdf
:When having a shell prompt that prints the version number of
ruby
andnode
, this adds half a second to each command!I love the idea of
asdf
but I don't want to give up version numbers in my prompt so I won't be usingasdf
in its current state unless I can find a workaround.Any ideas or advice would be welcome - thanks
Environment
OS:
OSX
asdf version:
v0.4.2
The text was updated successfully, but these errors were encountered: