Improving Bash performance
I have gradually noticed the degradation of my terminal’s performance when working with relatively large Git repositories. I use Bash as my Unix shell and keep my dotfiles organised, so it’s relatively easy to investigate and improve performance.
Improving git-status
The first thing I noticed was the poor performance when showing the working
tree status with git status
.
It took 2.34 seconds to enumerate untracked files. 'status -uno'
may speed it up, but you have to be careful not to forget to add
new files yourself (see 'git help status').
I’d still like to show untracked files, so the solution was not to use other
options but to improve the performance. I tried some commands like git prune
or
git gc
with no improvement. I also discovered some relevant options when working
with large repositories, one of the most interesting being feature.manyFiles
, which enables config options that optimize for repositories with many files in
the working directory.
$ git config feature.manyFiles true
But what really has made a substantial difference is Scalar. Scalar was created by Microsoft and they want to accelerate the Git workflow, no matter the size of the repository. The tool sets advanced Git config options, maintains the repositories in the background, and helps reducing data sent across the network.
I’ve installed the tools:
$ brew tap microsoft/git
$ brew install --cask scalar
$ brew install watchman
And then, from the working directory of the Git repository, I’ve registered it:
$ scalar register
Although I sometimes have problems with watchman invocations.
Improving Git prompt status performance
Another element with very poor performance is my prompt (f982e10)
, which uses the __git_ps1
function:
PROMPT_COMMAND="__git_ps1 "'${USR_COLOR}${USR}${OFF}@${HOST}:${LPURPLE}${DIR}${OFF} "\$ " "{%s}"'
The main problem is that if you want to see if there are untracked files,
then you have to set GIT_PS1_SHOWUNTRACKEDFILES
to a nonempty value, but this check
consumes a lot of time.
Since git-status now has a good performance, I’ve created a custom function
which provides similar functionality to __git_ps1
based on the output from
git status
. This is my new prompt (1d0df6d):
PROMPT_COMMAND="__system_prompt_command"
Improving Bash startup time
The last thing I’ve improved is the startup time of the shell. As I have my dotfiles split in several files, I’ve re-used an idea from even faster bash startup to profile each file:
for file in $(__system_dotfiles_dir)/system/*; do
TIMEFORMAT="$file: %R"
time source $file
unset TIMEFORMAT
done
Finally, I’ve removed redundant Bash completions that I was loading, changed
$(brew --prefix)
with the absolute path, and updated the rbenv initialization:
- eval "$(rbenv init -)"
+ eval "$(rbenv init --no-rehash -)"
+ (rbenv rehash &) 2> /dev/null
February 13, 2021 | @ArturoHerrero