Can I mark a write as "non-elidable", without making it volatile?
[[ Edit: There is a good chance I've misunderstood how syscalls and optimisation work. Perhaps none of this is even necessary? I will keep the rest of this as I posted it, but I highly recommend reading the comments! ]]
I have a use-case where an owned struct is located in a memory-mapped page, meaning its location in memory is a mutable buffer that the OS uses to shuffle data between disk and RAM. It will eventually be written to disk using msync.
The problem I'm facing is that when some fields of the memory-mapped struct are written but never read by my program, the Rust compiler is clearly allowed to optimise away the "unnecessary" writes. That would be bad for me, because I want even the writes that are never read to carry through to the page, so they can later be read by the kernel and written to disk. In other words, those writes are read, just not in a way that the compiler can see, or that I know how to explain to the compiler.
So far, my workaround is to use ptr::write_volatile;
The compiler shouldn’t change the relative order or number of volatile memory operations.
In other words, the compiler has to always actually do volatile writes, in the given order, exactly once per call, no optimisations allowed.
That works, but it's way too strong, like sawing a plank in half using a nuclear bomb. I want the compiler to reorder and coalesce the writes! I want it to keep intermediate values in processor cache, rather than taking the full trip to main memory every time! I don't need the writes to be volatile, I just need them to exist.
I guess what I'm looking for is some sort of "hey compiler, pretend that I'm side-effectfully reading these fields now, but don't actually read them" marker function, which would prevent preceding writes from being optimised out, but without incurring the penalty of volatile.
Does this exist?
The closest hacks I can think of:
- Actually read the values, and use them for something that is obviously a side-effect, like writing them to stdout. This would work, but it would also destroy performance even more so than
volatile. (What's the cheapest possible side-effect?) - Use inline assembly to read the fields into registers or whatever that aren't used. That would have a small performance penalty, and might obfuscate the situation enough for the optimiser to feel unsure about whether the values are used or not, and decide to keep the writes in. But it also seems like a fragile hack that will break the moment some future LLVM version decides to optimise across the inline assembly boundary. (I don't actually know very much about this. Is Rust allowed by spec to optimise there?)
I would appreciate any pointers, or related possibly useful bad ideas.