-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Macro for expression noalias hints #19658
Comments
Motivating example that does not require loading Celeste:
Looking at the IR:
We see a number of redundant stores to |
I should clarify that I intend the assertion to apply to the entire scope of the local variable, not just after the assertion. |
related issue: #8087 |
@vtjnash feels strongly that these semantics are too strong and that instead, the alias set should be more directly described and properly scoped wrt to normal control flow. Thus, let me suggest the following:
With the assertion being an actual assertion in that it throws an error. After this assertion the compiler can rely on regular alias analysis (assuming it can figure out the check ok if it should). I still feel like there should a command line switch to elide the check, but that feature could then be implemented using an llvm pass that looks at the generated alias check and transforms it into metadata. |
The obvious problem with this is of course that the check is at least |
In particular, the problem I have with mandating this is the following:
Now, I feel like it would be perfectly reasonable to want to assert Ideally the semantics would allow only emitting the check if the assumption was actually used, but I think that would be difficult, both semantically and from an implementation standpoint. |
I suppose at least for the array case, we could assert that their buffers are not shared (since we do keep track of that). That would only cause a linear number of checks (though still has the problem of unnecessary checks). |
Doesn't prevent the array to be used in multiple places? |
Yeah, I suppose that's true. |
Eigen does this with a C.noalias() = A * B; // where A, B and C are Eigen Matrices Note that with Eigen, the result of Maybe this idea also makes sense in Julia. Instead of saying that several variables don't alias as in #19658 (comment) (including all their fields), one could instead tell the compiler that a certain expression doesn't alias. Applied to the example from #19658 (comment), perhaps something like Here's an example where annotating sets of variables with data = rand(3, 9)
A = view(data, :, 1:3)
B = view(data, :, 4:6)
C = view(data, :, 7:9)
A[:] = B * C # doesn't alias, but the 'parent' field of the SubArrays A, B, and C is the same Array I'm also not sure whether having the compiler check that aliasing really doesn't occur is really necessary, or feasible. It feels very similar to |
The problem I see with that is that's not entirely clear what such an annotation would mean when applied to an expression. There's not really an obvious semantic to apply here, because the operators can be arbitrary functions. Eigen can get away with it, because the operations and operands are limited, but that's not the case in Julia. |
@vtjnash and I have discussed this some more and we discussed that perhaps a lexically scoped const annotation would be easier to reason about and implement. The options are pretty much as follows: Option 1
lowers to something like
Inside an alias scope, all stores do not alias any loads from Const arrays (and it would be illegal to write to the underlying memory). Option 2Have functions implicitly be an alias scope, and you can manually create Const wrappers, which are valid with respect to their scoped:
Though I suppose this would mean the same thing.
The second option seems somewhat nice, but I fear it will get confusing. The first option quite clearly delineates in what region the const applies, while the second option is rather implicit. |
The first option seems to fit in with existing performance macro annotations very nicely. |
It was implicit, but I should perhaps mention it explicitly. One major advantage of the |
Even though it's a departure from the way we've done a lot of performance annotations, I like the explicitness of the |
I think the |
Yes const would be relatively first class. Functions would be an implicit alias scope, but you could also declare alias scopes directly with a macro (which would cover the numerical integration example). I'm very much concerned about the nonlocality of the semantics though. |
This is the real reason to favor the macro approach – to avoid exposing anything non-lexical. |
Having played with this a whole bunch, I find the |
I’m a bit concerned by this when I see Hal saying that LLVM doesn’t support doing this for C due to observed correctness and performance issues (i.e., it sounds like they currently get the wrong result (wrong answer, slower) in the presence of inlining or operator overloading occurring, and that fixing that gave uniformly even worse performance (because the correct annotation seems it must prohibit many more important optimizations). Ref http://llvm.1065342.n5.nabble.com/llvm-dev-alias-scope-and-local-restricted-C-pointers-tp121373p121385.html for the latest comments I’ve seen about this.) He says there that they’re working on improving it, and interested in new ideas though. It sounds like he expects that it will never really work for C++ though, due to the ability to define constructors and operators—something that’s obviously very relevant to Julia! |
I'm increasingly in favor of a model where you can |
99% seems like a high over-estimate. It won't cover anything differential equation related. For example, with issue #28126 it wouldn't be applicable since the arrays are built iteratively: Maybe that thought holds for more computer science applications, but the general workflow in scientific computing seems to be:
For step 2 to be correct we have to setup the cache arrays so that they don't alias. But the compiler doesn't know that, so what we would need is some way to tell the compiler that. |
@Keno: what you're talking about reminds me of John Rose's "larval objects" idea: https://blogs.oracle.com/jrose/larval-objects-in-the-vm Implementation-wise, if we could convert a mutable value to a corresponding immutable one in a usually-zero-copy fashion, that would be what we want. Another way to approach that might be to be able to mark the mutable version is "illegal to use" and reassign the memory to the immutable version. Unfortunately, I can't think of a way to do that without either forcing a flag check on each access or using memory protections and faults. |
Hadn't seen that before, but yeah, it's the same concept. Implementation to be worked out, but I think something like it is promising. Note that I do think it makes sense to be able to go both ways (with the compiler eliding if possible, copies if not). |
We are missing significant optimization opportunity for small constant-size loops. For such loops it is not profitable to generate run-time alias checks, but we still loose significant performance due to lack of vectorization. I'm proposing introducing a macro to annotate variables that are known not to alias, similar to
restrict
in C. My initial thought is something like this.For generic structs:
I think these semantics are pretty strong and may be weakened over time, but it seems like an ok place to start. Eventually we may want to introduce more first class support for non-aliasing arrays (which array almost are, were it not for sharing by reshaping etc).
Motivating benchmark is jeff-regier/Celeste.jl#483
The text was updated successfully, but these errors were encountered: