SvenGroot commented on 28 Sep 2018 •
edited
I wish this problem was as easy as saying "NTFS is slow" or "DrvFs is slow". We've long since gotten all the low-hanging fruit and are left with what is essentially "death by a thousand cuts," with no single component responsible for our (lack of) performance, but with lots of different things contributing.
The IO subsystem is architected very differently in Windows than it is in Linux, with different goals in mind. Some of the major differences we have to deal with are:
Linux has a top-level directory entry cache that means that certain queries (most notably stat calls) can be serviced without calling into the file system at all once an item is in the cache. Windows has no such cache, and leaves much more up to the file systems. A Win32 path like C:\dir\file gets translated to an NT path like \??\C:\dir\file, where \??\C: is a symlink in Object Manager to a device object like \Device\HarddiskVolume4. Once such a device object is encountered, the entire remainder of the path is just passed to the file system, which is very different to the centralized path parsing that VFS does in Linux.
Windows's IO stack is extensible, allowing filter drivers to attach to volumes and intercept IO requests before the file system sees them. This is used for numerous things, including virus scanning, compression, encryption, file virtualization, things like OneDrive's files on demand feature, gathering pre-fetching data to speed up app startup, and much more. Even a clean install of Windows will have a number of filters present, particularly on the system volume (so if you have a D: drive or partition, I recommend using that instead, since it likely has fewer filters attached). Filters are involved in many IO operations, most notably creating/opening files.
The NT file system API is designed around handles, not paths. Almost any operation requires opening the file first, which can be expensive. Even things that on the Win32 level seem to be a single call (e.g. DeleteFile) actually open and close the file under the hood. One of our biggest performance optimizations for DrvFs which we did several releases ago was the introduction of a new API that allows us to query file information without having to open it first.
Whether we like it or not (and we don't), file operations in Windows are more expensive than in Linux, even more so for those operations that only touch file metadata (such as stat). The costs of these operations are spread all over the place, from Object Manager and IO manager, to the filters, and NTFS. If it was as simple as saying "NTFS is slow," we'd simply spend a release optimizing NTFS (and we have spent time doing just that), but our costs are so spread out over many components that there just isn't a silver bullet anymore. And we can only ever address in-box filters; who knows what third party filters are doing. We do work with the vendors of those filters to try and improve things. For example, when we introduced the new API to query file information without opening it, we needed filter drivers to support that. We needed to make sure we didn't break the system if some installed filters didn't support it (basically by falling back to a open/query/close operation). Making sure everybody supported this so you, our users, got the maximum speed benefit from that change took a lot of time and effort. The same thing is true for something like the case-sensitive directory work; we had to make sure our filter ecosystem could handle this new behavior.
When Rich was talking about app behavior above, he wasn't trying to blame the apps for behaving that way. These apps were written on a system where file system operations are incredibly fast, and we're trying to run them, unmodified (unlike e.g. Git for Windows which tries to optimize its access patterns to better fit the Windows way of doing things) on a system that, unfortunately, is not as fast.
And it's not even as simple as saying "Windows is the cause of the slowness" either, since WSL does play a role. Most notably, Windows path parsing is very different than Linux path parsing (and, as I said above, is the responsibility of the file system, so can differ between file systems, while on Linux it's centralized). Linux apps expect the Linux behavior, so we have to carefully emulate that behavior, and unfortunately that means sending more operations down to the file system. Something like a stat call, which in Linux can be served entirely from the kernel's own cache if you're lucky, for WSL requires sending multiple file system requests, all of which have to traverse the entire filter stack. We've done a lot of work, even as recent as the upcoming 1809, to reduce the amount of extra work WSL has to do. But the differences between Linux and Windows's API mean there'll always be some extra work, at least.
Basically, this isn't a simple problem, and everything we do involves multiple components, and often involves working with Microsoft's partners if the changes affect filter drivers.
That doesn't mean we've given up either. We're still making changes, and are actively exploring new avenues to make WSL faster, and thus more useful, for you. Of course, I wish I could tell you exactly what we're doing and what our timeline is, but that's unfortunately not the corporate reality we live in.