Replies: 2 comments
-
|
We definitely don't want to overload developers with another magic keyword or attribute for a very niche performance optimization (which, in this case, is potentially dangerous and memory-unsafe). At best, this could be part of an inter-procedural optimization, but the JIT doesn't do any of that today; we already have a list of things we would try first if we were to implement it. Even low-level languages avoid introducing hints like this (for example, C++ has no Generally, the JIT can already make smart assumptions. For example, it knows that two object references are either identical or will never alias, or that a ref struct/byref will never alias with the GC heap. |
Beta Was this translation helpful? Give feedback.
-
|
I think the desire for this is natural, but in practice a general |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I was hoping this might start an interesting, casual conversation. I don't actually propose this -- just wondering what people might think.
I know people like @AndyAyersMS of the world are working on alias improvements and future proposals have been going on about, but I never found any discussion on adding an alias hint construct. It's always about improving interprocedural analysis.
Quick Background for those who never heard of aliasing in CS
When a method accepts multiple references, the JIT often has no way to know whether they point to distinct storage. It therefore has to assume that a write through one reference may change the value observed through another. That assumption is required for correctness, but it can prevent:
[End of Background]
I was thinking about how C and C++ compilers have mechanisms such as
restrict,__restrict,__restrict__, and loop dependency directives that let programmers provide stronger aliasing guarantees. But they're not harmless optimization suggestions. If the program violates the contract and the optimizer relies on it, the result may be incorrect behavior or display the wrong values (e.g. a stale read RAW, WAR, WAW)SO: Thoughts on C# having narrowly scoped equivalent that is restricted to
unsafecode? I wonder what hardcore engineers on the runtime like @stephentoub and others might think about having something like this in their toolbelt for those heavy hot path areas.Would the underlying capability be useful enough to justify exposing it?
A minimal example
Consider these two methods:
The first method must support this perfectly legal call:
Because
xandymay refer to the same location, assigning42throughymay also changex.The .NET 10 JIT generates:
The important one is the final load:
The JIT cannot reuse the original value already in
eax, because the store throughrsimay have changed the value at[rdi].For the by-value version, the JIT can prove that the store through
ycannot alterx:It keeps
xin a register and computesx + xwithout reloading anything.This is not the JIT making a bad decision. It is generating the only generally correct implementation allowed by the method signature.
The signature communicates:
But it cannot communicate:
The same problem inside a loop
The cost is more visible when the lost alias information forces a load on every iteration:
The relevant part for aliasing version is:
The factor is reloaded through
rdion every iteration:That reload is required because this is legal:
The first array store changes
values[0], which also changesfactor.The ByVal version differs at exactly the point that matters:
Instead of loading through a pointer, the JIT retains
factorinedi:This example only demonstrates one eliminated load. The larger potential value could presumably be cases where alias uncertainty prevents loop-invariant code motion, struct promotion, or vectorization.
What could a C# mechanism look like?
Illustrative examples below. I'm no language designer😊
Parameter attributes
Something relational would probably be required:
A simple
[NoAlias]attribute on one parameter may be too ambiguous. Aliasing is a relationship between storage locations, not necessarily an intrinsic property of one parameter.A keyword
Possibly something allowed only in unsafe code:
OR:
An
Unsafeintrinsic?A checked fast path?
If the JIT understood that the second path operates on non-overlapping ranges, it could optimize that path without relying on an unchecked programmer promise.
This seems more compatible with managed-code semantics; although it introduces a branch and may require method cloning or path-sensitive alias information.
The Rust comparison
Rust gets strong aliasing information from its ownership and borrowing rules. A mutable reference generally carries an exclusivity guarantee that the compiler can use when optimizing.
I am not suggesting that C# adopt a borrow checker. LOL! That would be a fundamentally different language model.
The narrower question is whether C# could expose a small, opt-in subset of that information for performance-sensitive code without requiring the entire language to adopt ownership semantics.
Existing features such as
ref struct,scoped,in, andref readonlyalready communicate limited facts about lifetime and mutation. None of them, however, expresses that two references or memory ranges are non-overlapping.A narrowly scoped no-alias contract might provide some of the optimization value of stronger ownership systems while remaining confined to expert or unsafe code.
On the other hand, it may be that once the semantics are specified rigorously, the feature turns into a miniature and poorly enforced borrow system--which would make it a terrible idea.
Beta Was this translation helpful? Give feedback.
All reactions