I’ve noticed that starting a new fish shell takes at least 2 seconds, and it’s really starting to annoy me.
Profiling
I decided to profile my config.fish
script. Since I couldn’t find any profilers, I resorted to the oldest method: putting print and time statements everywhere in my fish script. I quickly found that the main bottleneck was the section initializing my env tools, so I wrapped it in the time
command to measure the execution time.
time pyenv init - | source
# _______________________________________________________
# Executed in 264.81 millis fish external
# usr time 80.20 millis 1.47 millis 78.73 millis
# sys time 140.85 millis 1.82 millis 139.04 millis
time goenv init - | source
# _______________________________________________________
# Executed in 2.96 secs fish external
# usr time 0.70 secs 1.65 millis 0.70 secs
# sys time 1.83 secs 2.09 millis 1.83 secs
time direnv hook fish | source
# _______________________________________________________
# Executed in 15.11 millis fish external
# usr time 2.68 millis 83.00 micros 2.60 millis
# sys time 5.30 millis 172.00 micros 5.13 millis
time direnv export fish | source
# _______________________________________________________
# Executed in 11.63 millis fish external
# usr time 2.64 millis 25.00 micros 2.62 millis
# sys time 3.57 millis 360.00 micros 3.21 millis
time rbenv init - --no-rehash fish | source
# _______________________________________________________
# Executed in 51.44 millis fish external
# usr time 16.64 millis 217.00 micros 16.42 millis
# sys time 29.18 millis 387.00 micros 28.79 millis
What the hell: goenv
takes about 3 seconds to start. I’m not sure what it’s doing, but it’s too long to continue using it. By the way, pyenv
also doesn’t look great - 0.3 seconds.
Searching for alternatives
After researching alternatives to goenv
, I came across asdf
and mise
, which caught my attention. These are tool version managers that can install and switch between multiple versions of different languages and tools. They can replace not only goenv
, but all env tools.
A quick comparison showed that mise
is a modern alternative to asdf
, written in Rust, and it seems to have much better performance. At least, that’s what the developers claim. Their documentation even has a section describing how to install Rosetta versions of tools, so they sold me on it.
Integration
Step by step, I replaced goenv
, pyenv
, direnv
, and rbenv
with mise
. The process was very simple:
- install
mise
:brew install mise
- delete initialization lines from
config.fish
of all *env tools - put
mise activate fish | source
in theconfig.fish
file
That’s it. After that, just go to your project folder and type: mise install
and you will be happy.
Conclusion
I cleaned up my fish startup script by replacing all these tools with one line that takes only 0.05 seconds to execute. And completely moved to mise
, as it’s a drop-in replacement for all *env tools. It even detects and respects .<language>-version
files, and it just works seamlessly.
time mise activate fish | source
# ________________________________________________________
# Executed in 44.73 millis fish external
# usr time 8.40 millis 0.61 millis 7.79 millis
# sys time 10.83 millis 1.53 millis 9.30 millis
I can’t even recall a case when one tool replaced a bunch of others and did it in a much more intuitive and faster way. Really great job!