Faster git status under WSL2

Use git.exe when working in repositories under /mnt/โ€ฆ
Use git.exe when working in repositories under /mnt/โ€ฆ

With WSL2 filesystem performance degraded for the mount points of the Windows host. Here's a tip to speed up git status again.

tl;dr: jump to the solution.

For personal projects I work on my PC. And to my own surprise nowadays solely on my Windows 10 installation. While I still have my Linux on another hardrive, I haven't booted into it for half a year now. The reason why it is possible, specifically if you do have Linux focused projects, is WSL (Windows Subsystem for Linux). While I have experimented already with WSL 1 for some time, I switched to WSL 2 since it became generally available in Windows 10 Version 2004 (apparently it was backported to 1903 and 1909).

My main reason for switching to WSL2 was much better Linux support (kernel and syscall stuff), and for a long time everything was fine. I didn't even notice the performance hit across OS file systems.

But lately something was bothering me. And it started with a related but independet issue in my favourite shell prompt tool starship: git_status became extremely slow in repositories of some size. Since it was my prompt every command execution in a git-managed project was slow, better yet, it took just seconds until the prompt came back. First I didn't know what the issue was, until I discovered issue 1617 (and in another issue it is mentioned that an update to v0.45 will make it even slower, as well as a solution idea to mitigate it).

So I turned the extension off and ran git status manually. But lo and behold: that was still very slow!

hyperfine benchmark of git status, Linux vs Windows
(click image for bigger version)

10 seconds (Linux git) vs ~400 ms (Windows git.exe)!

Aside: hyperfine is a nice tool to benchmark CLIs and shell scripts/functions.

What was the problem? Also under Windows in a PowerShell with the Windows version of git, no performance penalty was noticable. I searched around, if there was an issue with git under WSL2.

And then I got reminded about the one line of the comparison table mentioned above:

FeatureWSL 1WSL 2
โ‹ฎโ‹ฎโ‹ฎ
Performance across OS file systemsโœ…โŒ

Well, the row alone does not really explain how much of a problem this might be.

I was not alone, in the GitHub repo of WSL I found issue 4401 which was quickly pointing to 4197. Within the lengthy thread there was a very detailed explanation of the underlying problem, some key statements:

Windows files are now accessed across the VM boundary, however. [โ€ฆ] In WSL2, every operation has to send data to the host, exit the VM, wait for the host to perform the operation (which still involves emulating Linux behavior and the cost of Windows IO operations), send data back to the VM, trigger an interrupt in the VM, schedule the virtual processor to run, and continue executing in the VM.

Essentially, Windows files are now a "remote" file system.

To make matter worse, to ensure the same behavior as WSL1, we don't use any caching.

Without caching, it means every "stat" operation has to make a round-trip to the host (multiple, actually, partially because of how Linux VFS works, and partially because of how 9p works).

Leaving with one recommendation in the end:

So yes, for now the most important thing is: if it's at all possible, store your projects in the Linux file system in WSL2. This will be much faster than anything in WSL1, and gives you the full benefit of WSL2.

Sadly that's not an option I want to follow, as I love to work on my project across OS boundaries. So I have to wait until a performance improvement will land in WSL2 hopefully.

Since git is the only very noticable issue for me, I only need a fix for that for now. Luckily someone else had a great idea how to alleviate the problem (discovered through a tiny detour to someone else's dotfiles).

I came up with a dumb but useful workaround for this specific case. It works because windows git is faster against ntfs and I don't need any linux specific things for git at least.

I took the snippet and adjusted it for my environments.


Solution

Call the Windows Git (git.exe) command when working in a /mnt folder which is a Windows host (NTFS) mount.

Bash, ZSH, and friends

# use Windows' git when working under C:\ drive
function git() {
  if $(pwd -P | grep -q "^\/mnt\/c\/*"); then
    git.exe "$@"
  else
    command git "$@"
  fi
}

Fish

function git --wraps git
  if pwd -P | grep -q "^\/mnt\/c\/*"
    git.exe $argv
  else
    command git $argv
  end
end

Some explanations:

pwd -P

โ€ฆ returns the current physical working directory with all symlinks resolved. This is great since I have some symlinks for my dev folders, so all machines and VMs have a common structure (well, except for Windows itself).

grep -q "^\/mnt\/c\/*"

โ€ฆ searches for the pattern matching paths starting with /mnt/c quietly (-q), the exit status will be used for the if check. Adjust the pattern if you need different drive letters.

git.exe "$@" # fish: git.exe $argv

โ€ฆ calls the git from Windows. This is one of the benefits of WSL over usual virtualization approaches: Windows allows to execute commands within WSL environments. IIRC it basically executes directly on the Windows host and only communicates back and forth. So git is called on the C:\... folder and no filesystem penalty has to be paid.

command git "$@" # fish: command git $argv

Since we're masking the git command with our function, we need a way to call the git executable directly. The linked solutions use explicit absolute file paths, but I don't want to think about if a distro installs it under /bin, /usr/bin, or elsewhere, I prefer to let the shell figure out where the executable is. command is made for that. Reminder to myself: use command more often!


So, my starship prompt is not fixed yet, on the other hand I can live without the git status info. Theoretically I could also live without this nifty shell function, since I moved most of my repo management into my VS Code workflows. But sometimes it is nice to do some tasks in a plain shell.

Also if you're interested in my current development environment on my PC, ping me on twitter and maybe I'll post about it here.