https://danpker.com/posts/faster-bash-startup/ Daniel Parker Posts Tags RSS Github Faster Bash Startup 2 Aug 2020 * #bash --------------------------------------------------------------------- My Bash start-up time is around 1.7 seconds. This doesn't sound like it's too bad, but it feels like hours when you want to open a new window to only run one command. The first step is to run a profile of your Bash start-up, to try and find out what exactly is taking so long. How to profile a bash shell script slow startup? This should give you a timestamp against each Bash command that gets run, which you can use to work out which ones are taking the most time. For me, there are a few things that were highlighted. Note: Everything here will be specific to my setup (MacOS, Alacitty, Bash, Tmux) and so may and probably will differ. I tested the difference between each change by using hyperfine. hyperfine 'bash --login' Dynamic paths One of the big culprits for slowness is trying to be too clever. For example, Homebrew has a prefix directory where it installs everything, and environment variables that are set are set relative to this directory. For example, you might have some code that looks like this: if [ -f $(brew --prefix)/etc/bash_completion ]; then . $(brew --prefix)/etc/bash_completion fi To allow for changing the default prefix directory, there is a command brew --prefix that will resolve to where the prefix directory is. However, this command can be quite slow and just replacing it with a hardcoded path can give you a decent speed up: Benchmark #1: bash prefix.sh Time (mean +- s): 289.9 ms +- 4.5 ms [User: 178.9 ms, System: 103.6 ms] Range (min ... max): 285.4 ms ... 300.7 ms 10 runs Benchmark #2: bash no_prefix.sh Time (mean +- s): 236.1 ms +- 2.4 ms [User: 157.7 ms, System: 75.9 ms] Range (min ... max): 231.6 ms ... 241.8 ms 12 runs Summary 'bash no_prefix.sh' ran 1.23 +- 0.02 times faster than 'bash prefix.sh' This is even more significant if you have a prefix command that specifies a particular library. E.g: if [ -f $(brew --prefix openssl) ]; then OPENSSL_PREFIX="$(brew --prefix openssl)" export OPENSSL_INCLUDE_DIR="${OPENSSL_PREFIX}/include" export OPENSSL_LIB_DIR="${OPENSSL_PREFIX}/lib" fi In this case, using a hard coded path has a significant speed up. Benchmark #1: bash prefix.sh Time (mean +- s): 1.095 s +- 0.015 s [User: 627.0 ms, System: 444.6 ms] Range (min ... max): 1.079 s ... 1.131 s 10 runs Benchmark #2: bash no_prefix.sh Time (mean +- s): 3.1 ms +- 0.4 ms [User: 1.2 ms, System: 1.0 ms] Range (min ... max): 2.5 ms ... 5.4 ms 389 runs Warning: Command took less than 5 ms to complete. Results might be inaccurate. Summary 'bash no_prefix.sh' ran 358.57 +- 47.07 times faster than 'bash prefix.sh' Lesson: Avoid $(give_me_a_path) where possible by using a hard coded path. Especially avoid brew --prefix . Bash completions The other major slow down in my Bash start-up was Bash completions. Benchmark #1: source .profile Time (mean +- s): 208.6 ms +- 5.7 ms [User: 90.0 ms, System: 115.6 ms] Range (min ... max): 203.7 ms ... 223.7 ms 13 runs Benchmark #2: source .profile && source /usr/local/etc/bash_completion Time (mean +- s): 442.3 ms +- 3.3 ms [User: 240.2 ms, System: 198.4 ms] Range (min ... max): 438.1 ms ... 449.2 ms 10 runs Summary 'source .profile' ran 2.12 +- 0.06 times faster than 'source .profile && source /usr/local/etc/bash_completion' As you can see above, adding completions doubles the start-up time of my Bash session. I honestly don't know if I have ever even used them, so it was an easy trade-off for me to make by removing them for a bit more speed. If I had used them I would have thought more carefully about it. Conclusion Using variations on the above I've managed to shave quite a bit of time from my Bash startup (ignore the name change): Benchmark #1: source ~/.bashrc Time (mean +- s): 1.669 s +- 0.025 s [User: 924.4 ms, System: 706.6 ms] Range (min ... max): 1.644 s ... 1.728 s 10 runs Benchmark #1: source ~/.profile Time (mean +- s): 216.2 ms +- 5.9 ms [User: 94.2 ms, System: 119.5 ms] Range (min ... max): 210.2 ms ... 234.0 ms 13 runs I could keep tweaking in an attempt to make it even faster, but anything else at this point is likely to suffer from diminishing returns. I mean, it has to take some time. (c) 2010 - 2021 Daniel Parker