This seems to describe me. I am tempted to rewrite those programs more idiomatically. Thank you for pointing this out.
I have cut back: x64 single core & x86 quad core are no longer measured, x86 single core updates are not frequent, and x64 quad core updates are less frequent than they used to be (although more frequent than the chart dates might suggest).
I've kind-of been hoping the hardware would fail -- but someone would probably gift me a new machine :-)
From my perspective "defence of the benchmarks game" is a matter of saying "Keep it in perspective" and "Keep it in context" until we do ;-)
In C, if you write code like this:
void dostufftotwoarrays(int* a, int* b);
Then maybe b is equal to a + 3, so your compiled code has to assume whenever it writes to an element of a, it might have changed an element of b, and so reload b from memory. Alternatively, it can do a bunch of tests at the start of the function, and compile multiple copies of the function for different situations.In FORTRAN, this isn't possible (you don't tend to pass around raw pointers), so it is much easier for the compiler to reason about what objects might be in the same place, and which aren't.
The restrict keyword goes some way to fixing this in C, but is tricky yo use correctly, and also doesn't solve all problems.
This doesn't affect aliasing in Fortran at all. Aliasing is simply prohibited, which leaves the compiler free to generate code that gets the wrong answer, if the programmer screws up and has actual aliasing at run-time.
mypair* restrict position = &object->pair;
float* restrict position_x = &position->x;
Is undefined behaviour, as you can't take a restricted pointer to a child of a restricted pointer in the same scope (the rule isn't quite that simple, but that's part of it). In general, rules about pointers to pointers get complicated, and I've had trouble with them in the past.In general, it's a good idea to try to only use restrict for "leaf" pointers, not intermediate objects, but that does block some optimisations.
There is also the obvious problem of people getting carried away, and marking something 'restrict' where they shouldn't have.
While 'restrict' is very easy to use on simple structures (like just int*), if you look around you'll find very little, if any, material about using it for complicated nested data structures.
As far as I know Fortran, at least in its original form, enforced the latter (that was fairly easy to do, as it didn't have dynamic memory allocation, pointers or recursion (the compiler determined the location in memory of all variables))
You can totally call a subroutine with several arguments being the same thing, as long as you don't do anything bad inside the subroutine.
I'm aware of this because I tried to get the PathScale compiler team to add a warning for incorrect aliasing. That wasn't possible. But I did get a flag which made the compiler obey C aliasing rules for Fortran, and it was part of the "got the wrong answer? run with these flags and if you get the correct answer, your program is not standard-conforming" thingie.
I claim it is only a promise, you claim you can call it with identical arguments. I don't see a contradiction; making a promise isn't the same as keeping it.
Similarly, your argument that it is fine to break that promise as long as you don't do anything bad inside the subroutine, I read as "you sometimes can get away with breaking a promise", but again, that's not the same as not making the promise.
What am I missing?
int chooseone(restrict int* a, restrict int* b, bool c) {
if(c) return *a; else return *b;
}
Here, we can pass the same pointer for a and b, because only one of them is ever accessed in any call to the function."An object that is accessed through a restrict-qualified pointer has a special association with that pointer. This association, defined in 6.7.3.1 below, requires that all accesses to that object use, directly or indirectly, the value of that particular pointer."
So, it hinges on the meaning of 'accesses'; it is a runtime thing, not a compile-time thing.
> I would expect two compiled languages [...] to perform almost exactly the same when they're doing nothing but arithmetic operations [...]
Different languages are going to have different memory models which is going to result is vastly different performance. E.g. a GC vs reference counting vs linear types. If you are doing arithmetic on something that needs to be managed (e.g. an array), you'll see differences.Some language features close the door for optimizations. E.g. the ability to dynamically add or remove methods to an object/class can make method dispatch more or less expensive.
Finally, some languages have undefined behavior (e.g. what happens when there's an overflow or a division by zero) while other languages want specific behavior for the edge cases. This all affects the performance of the resulting code, even for basic arithmetic operations.
In this case though, C doesn't have garbage collection or OO features, and (I believe) has unspecified behavior for arithmetic exceptions. I assume Fortran does the same (since it's comparably fast), so I guess I still don't really see where people expect the difference to come from.
As for what the differences are, try talking to a compiler engineer. The better ones love how Fortran features like the argument aliasing rule for 100% of code makes generating fast code easier. In fact that's one of my tests for compiler engineers in interviews; if they've worked on a C/C++/Fortran compiler and can't say anything good about the challenges and opportunities of Fortran optimization relative to C/C++, then they haven't really drilled down on performance issues!
I know as a long-time C programmer, there are small changes I can make to source that will result in big-time performance gains.
Could hinge on how tight their FORTRAN code is vs their C code...
Could also depend on how much love their respective machine code translations have been getting.
It is very easy for a physical scientist to write Fortran that performs well - the syntax looks very similar to the maths that is being modeled. It is a lot harder for a scientist to write tight C code.
Look and you might see measurements of several different C or C++ or … programs doing the same task with different performance.
https://benchmarksgame.alioth.debian.org/u64q/performance.ph...
Historically, C compilers just couldn't do such a good job at optimising, because C is pretty complex and has loads of undefined behaviour. Also, Fortran compilers have always been optimised for numerical computation.
Recently * , a lot of effort has gone into making C compilers produce very optimised code. In general, this is a good idea because humans are bad at optimising and also remembering every damned Intel vectorisation instruction.
So historically, Fortran was much faster, recently * with compiler advances in e.g. reordering and AMD64, it's possible C compilers beat Fortran compilers.
* edit: adjust your definition of recently to encompass the entire timeframe of Fortran's existance
Auto-vectorization? These implementations vary wildly between GCC and Intel.
Unless I missed something, this directly contradicts the quotes benchmark source[1], where C beat Fortran on all tests except one (where they were tied), and usually by a wide margin. Plus I believe gcc usually produces slower code than Intel's compilers (at least for C), so it's not clear that C-gcc and Fortran-Intel is a fair comparison.
But beyond that, I'm always confused as to why there is a significant difference. I would expect two compiled languages with reasonable compilers to perform almost exactly the same when they're doing nothing but arithmetic operations, since those are almost entirely determined by the CPU, not the cleverness or speed of the standard library algorithms. Can anyone enlighten me?
[1] https://benchmarksgame.alioth.debian.org/u64q/fortran.html