(no title)
vgatherps | 9 months ago
Iirc the work on safe transmute also involves a sort of “any bit pattern” trait?
I’ve also dealt with pain implementing similar interfaces in Rust, and it really feels like you end up jumping through a ton of hoops (and in some of my cases, hurting performance) all to satisfy the abstract machine, at no benefit to programmer or application. It’s really a case where the abstract machine cart is leading the horse
Arnavion|9 months ago
I've implemented what TFA calls the "double cursor" design for buffers at $dayjob, ie an underlying (ref-counted) [MaybeUninit<u8>] with two indices to track the filled, initialized and unfilled regions, plus API to split the buffer into two non-overlapping handles, etc. It certainly required wrangling with UnsafeCell in non-trivial ways to make miri happy, but it doesn't have any less performance than the equivalent C code that just dealt with uint8_t* would've had.
lilyball|9 months ago
CJefferson|9 months ago
I totally understand not wanting to promise things get zeroed, but I don't really understand why full UB, instead of just "they have whatever value is initially in memory / the register / the compiler chose" is so much better.
Has anyone ever done a performance comparison between UB and freezing I wonder? I can't find one.
wrs|9 months ago
Also, an uninitialized value might be in a memory page that gets reclaimed and then mapped in again, in which case (because it hasn’t been written to) the OS doesn’t guarantee it will have the same value the second time. There was recently a bug discovered in one of the few algorithms that uses uninitialized values, because of this effect.
vgatherps|9 months ago
dathinab|9 months ago
it kills _a lot_ of optimizations leading to problematic perf. degredation
TL;DR: always freezing I/O buffers => yes no issues (in general); freezing all primitives => perf problem
(at lest in practice in theory many might still be possible but with a way higher analysis compute cost (like exponential higher) and potentially needing more high level information (so bad luck C)).
still for I/O buffers of primitive enough types `frozen` is basically always just fine (I also vaguely remember some discussion about some people more involved into rust core development to probably wanting to add some functionality like that, so it might still happen).
To illustrate why frozen I/O buffers are just fin: Some systems do already anyway always (zero or rand) initialize all their I/O buffers. And a lot of systems reuse I/O buffers, they init them once on startup and then just continuously re-use them. And some OS setups do (zero or rand) initialize all OS memory allocations (through that is for the OS granting more memory to your in process memory allocator, not for every lang specific alloc call, and it doesn't remove UB for stack or register values at all (nor for various stations related to heap values either)).
So doing much more "costly" things then just freezing them is pretty much normal for I/O buffers.
Through as mentioned, sometimes things are not frozen undefined on a hardware level (things like every read might return different values). It's a bit of a niche issue you probably won't run into wrt. I/O buffers and I'm not sure how common it is on modern hardware, but still a thing.
But freezing primitives which majorly affect control flows is both making some optimizations impossible and other much harder to compute/check/find, potentially to a point where it's not viable anymore.
This can involve (as in freezing can prevent) some forms of dead code elimination, some forms of inlining+unrolling+const propagation etc.. This is mostly (but not exclusively) for micro optimizations but micro optimizations which sum up and accumulate leading to (potentially but not always) major performance regressions. Frozen also has some subtle interactions with floats and their different NaN values (can be a problem especially wrt. signaling NaNs).
Through I'm wondering if a different C/C++ where arrays of primitives are always treated as frozen (and no signaling NaNs) would have worked just fine without any noticeable perf. drawback. And if so, if rust should adopt this...
JoshTriplett|9 months ago
Abstractions like ReadBuf allow safe code to efficiently work with uninitialized buffers without risking exposure of random memory contents.
jcranmer|9 months ago
orlp|9 months ago
Some people simply aren't comfortable with it.
Currently sound Rust code does not depend on the value of uninitialized memory whatsoever. Adding `freeze` means that it can. A vulnerability similar to heartbleed to expose secrets from free'd memory is impossible in sound Rust code without `freeze`, but theoretically possible with `freeze`.
Whether you consider this a realistic issue or not likely determines your stance on `freeze`. I personally don't think it's a big deal and have several algorithms which are fundamentally being slowed down by the lack of `freeze`, so I'd love it if we added it.