Copy-on-write performance and debugging
devblogs.microsoft.com
devblogs.microsoft.com
https://devblogs.microsoft.com/engineering-at-microsoft/dev-...
"Copy-on-write (CoW) linking, also known as block cloning in the Windows API documentation, avoids fully copying a file by creating a metadata reference to the original data on-disk. CoW links are like hardlinks but are safe to write to, as the filesystem lazily copies the original data into the link as needed when opened for append or random-access write. With a CoW link you save disk space and time since the link consists of a small amount of metadata and they write fast."
It seems there is a MacOS implementation: https://github.com/dotnet/runtime/pull/79243
But it seems that this is .Net specific and not something that would speed up other build systems? It is confusing if this can apply to other build technologies other than .NET. Can it speed up TypeScript/JavaScript builds? Can it speed up Rust builds? Also what are the speed ups on these other platforms like MacOS and Linux?
Is this something that all build systems and all OSes would benefit from?
I guess this blog post for me raises more questions than it answers.
This is .NET specific insamuch as this is getting MSBuild to take advantage of ReFS features; but there's no particular reason why other build systems couldn't take advantage of ReFS in the same way MSBuild can take advantage of APFS. The main question is if the build system needs to make lots of copies of files that may not ever be updated. I imagine anything that does dependency fetching (especially Node/NPM) would benefit.
https://docs.npmjs.com/cli/v7/commands/npm-cache
I am not sure if it uses CoW to bring those packages into each project. If it did, that would be efficient and speed up "npm install" if the cache was warm.
DevDrive is not a derivative of ReFS, it is ReFS with some file system filter bits turned off among a couple of other things. DevDrive is a collection of features centered around ReFS for the purposes of speeding up file read/writes (think node modules).
Initially I was not aware of this and I was surprised when I have copied a directory with a total size greater than 50 GB and the copy was instantaneous. At first I believed that I had given some wrong command, but then I searched the XFS documentation and I saw that this was a new feature at that time.
ReFS, with block de-duplication and LZ4 compression has reduced the per-workspace footprint to around 10% of what it was previously. Decreased build times by around 5% and decreased archive, stage and package times by about 80% by deploying MSBuild SDK CopyOnWrite. I also moved the DDC onto the VHDX where the project resides which has further reduced the footprint of the project.
Windows 11 canary channel (still in canary I think) has a modified Win32 that supports CoW FileCopyEx. You can get similar gains by other means on Win10 and Win11 by using ReFS CoW aware utilities.
Have used XFS, BTRFS, APFS and others extensively over the years, so I am glad that Windows is finally getting in on the action.
That's not exactly confidence inspiring.
(The shared extents have to be immutable, because on non-CoW filesystems, filesystem locks apply to "byte ranges of inodes", not to extents or slices thereof; so extents could only be safely shared between inodes if they forced the inodes referencing them to act as if they were always reader-locked.)
You could even implement this on e.g. Linux ext4 today — you could consider extents immutable if they're part of an immutable (chattr +i) file that has no additional hard links; and you could prevent any files that are "sharing" immutable extents from being made non-immutable (where in the above, the syscall would create a file that is immediately immutable.)
This would basically result in the same semantics + efficiencies that you get with "composite uploads" in an object store.
---
Given a CoW filesystem, you could probably extend this concept to allow arbitrary CoW blocks to be explicitly referenced from file A into file B without any need for immutability — it'd just be an explicit "partial" reflink. (This is already possible for the simple A->B case, by starting with a CoW clone, and then overwriting the blocks that shouldn't be shared. But more complex cases like A+B+C->D above, aren't possible; nor is having those shared blocks be in a different position in the clone than they are in the original; and so forth.)
It wouldn't quite work like you're imagining with sendfile(2), though, because the CoW sharing could only occur at filesystem-block boundaries. You still wouldn't be able to use partial reflinking to optimize the operation of e.g. adding three bytes of header to a file (unless you also added BLKSZ-3 bytes of padding.)
I've never understood why filesystems don't easily support prepending data, or to truncate the start of a file.
It should be, as far as I can see, about as trivial to support as appending and truncating the end, and is something that comes up quite often in application code. Even if it is a bit more tricky, I think the benefits would be great in the cases where it's needed.
Instead applications are left having to rewrite the contents.
The real concern is block alignment.
See `FALLOC_FL_COLLAPSE_RANGE` and `FALLOC_FL_INSERT_RANGE` in `fallocate(2)`
Realistically I don't think that's going to happen very often. What would be the use case? The only case I can think of is something like tar where you pack up a bunch of files as a single file, and you usually do compression at the same time in that case.
Every time you need to extend the length of the field in the middle of your data (e.g., increase a variable-length-field in the header). Many file formats resort to trailer chunks specifically because of that (see ID3v2 of MP3 files, or ZIP). Or think about how SQLite's on-disk file format uses freelist of file pages: it's literally an embryonic file system in itself, built on top of the existing one.
Or hey — what about linking object files together? It's kind of like the merge sort, since you need to re-slice text/data/misc and then re-glue them. Traditional linkers would create several temporary files for each combined section and then merge those files together.
Windows having CoW makes my far fetched dream a possibility.
I tried that Dev Drive thing and I havent seen perf improvement when building C++ code, sadly.