Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> The performance difference comes from the fact that Fortran says aliasing is not allowed [...]

This is one of the legacies modern C++ is still wrestling with today, supposedly solved by Rust, Zig, Val and the likes.



It's true that there's no "restrict" inthe C++ standard, however all the major C++ compilers have extensions for it ("__restrict__") and have had for a long time.


https://gcc.gnu.org/onlinedocs/gcc/Loop-Specific-Pragmas.htm...

The #pragma GCC ivdep annotation also enables this in loops. C23 also has the [[unsequenced]] attribute, which supports pointer arguments for optimizations like these, unlike [[gnu::const]].


Except that it doesn't work and is basically WONTFIX since no C or C++ programmer ever uses it.

This is only very slightly exaggerated.


We use it all the time in audio code. How do you get the idea that it doesn't work?


Look at the gyrations the Rust compiler team has been going through for years to be able to actually use LLVM's noalias (which is a very desirable optimization in Rust where the borrow checker statically guarantees absence of aliasing in a lot of code).

I haven't kept up with the current state of things, but if you can finally liberally use noalias in LLVM without having to fear miscompilations, you probably have solely the increased significance of Rust to thank for that.


We use gcc so honestly I'm not sure how the difference goes. I'm glad for Rust making the whole ecosystem more usable regardless.


GCC had to spend some time getting things correctly because of gfortran.


It worked fine in Intel compiler. We used it a fair bit to sort out hot paths in C++ for high performance computing (where Fortran was the other main language). Now Intel have moved to LLVM as their compiler base I wonder if they've put some work into making it work there?


I've never had issues with it, personally. In what way does it not work?


Rust has aliasing guarantees. In theory, Rust code can pass noalias annotations to LLVM and become more optimized.

In practice, it took years for Rust access these optimizations because the noalias annotations kept hitting codegen bugs in LLVM. It was an area of the compiler that had not seen much active use before Rust, and they were causing a lot of dormant bugs to become issues.

The feedback cycle was also very long because LLVM didn't fix the problems quickly. I didn't pay close attention so I don't know if this is prioritization or because those fixes required major surgery, but Rust sometimes had to wait for major version bumps in LLVM before seeing if "this time was the last time."


Not just in theory. Rust does now mark almost all references as `noalias`, the exception being `&T` where `T` contains an `UnsafeCell`. The equivalent safe Rust function signature[1] would have all four references marked `noalias`, even though the `input` and `matrix` slices could alias.

How much the optimizer can take advantage of that is another matter, due to what you said. Doing a quick translation to more idiomatic Rust[2], it does hoist the accesses to `matrix` out of the hot loop, and does also seem to be a bit less moving stuff around compared to the C version, which I think is putting the `matrix` values in the right places, which Rust did before the loop.

[1] fn transform(output: &mut [f32], input: &[f32], matrix: &[f32], n: &i32);

[2] https://godbolt.org/z/5PPn3eh19


That's interesting. It also looks like with the `__restrict` specifier (https://godbolt.org/z/cvaYoc6zh), the clang code is similar regarding the matrix multiplication. The body of the vectorized loop itself looks identical, `.LBB0_4` in clang and `.LBB1_8` in rustc.


Swift ran into similar difficulties with noalias. Happily the bad cases I was tracking that inhibited optimizations were resolved in the last year or so. There’s probably still some lurking corner cases, but the overall situation is pretty good now.


Yup. But it is true now though so hopefully they’ve added the test cases to LLVM to make sure things continue to work correctly in the face of more and more optimizations being thrown at code.


That was, for the most part, just a problem with LLVM governance and development priorities. When the bugs were found in LLVM, GCC was examined and found to have a few similar bugs. GCC fixed these issues relatively quickly, but LLVM sat on them for years.

Normal C/C++ developers doing normal C/C++ development in GCC probably never saw any of those bugs.


That's utterly incorrect. It's used thousands of times in the linux kernel.


Hmm, I thought basically only Rust solves it. How do Zig and Val solve it?


Multiple-aliasing cannot exist in a language without reference semantics, such as Val (or whatever its new name is).


Zig doesn't solve it. I don't remember where but I know Andrew has mentioned that directly.


fortran is call by reference, it's all aliasing, you can even in some implementations change the value of literals

c is call by value, you have to at least try to alias


It took me days one time to figure out why 0 had adopted the value four.

The technicality is that Fortran is call by descriptor (not by reference, not by value). That's a powerful difference but a sharp two-edged sword.


Could you please explain how is it differs from the other two? Never looked into Fortran.


The Fortran standard does not specify anything about passing by references or value. Instead, it specifies the expected results. The compilers do it however they want or can, which means that they can pass pointers, values, copies, or pointers to copies depending on the situation.

Sometimes (most of the time, to be fair), the compiler can work out the best solution. Sometimes, the developer has to jump through some hoops to avoid unnecessary copies.


Fortran does not call by reference, and the term aliasing isn't used by the standard. It calls by what's usually called value-return or copy-in/copy-out, according to the "storage association" rules in the standard.

While it may be true that the rules aren't generally checked by compilers, Toolpack did it for pure Fortran77 in the 1980s, though I don't know how comprehensive it actually was.


my understanding is that in fortran, if two arguments alias each other, it is UB. So it is not that fortran doesn't have aliasing, it is just that the optimizer assume that it doesn't (but don't quote me on this!).


Arguments are allowed to alias if they are both inputs that will not be changed in the function -- technically if they are both "intent(in)".


INTENT(IN) is not required to allow aliasing of unmodified dummy arguments.


Yes, but in practice no compiler checks whether an argument with no INTENT is modified or not, or could alias with something else or not. So the compilers tend to trust the programmer. With INTENTS, it’s obvious and much easier to check.


Dummy argument aliasing has nothing to do with INTENT.


I believe the NAG Fortran compiler does check for some aliasing scenarios if not all. The argument intent is irrelevant.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: