On how "fast" C is, guys, sorry to say this, but in my experience actually (1) even assembler is not very "fast", (2) for much the same reasons C is not very fast, (3) often Fortran is faster than C, and (4) PL/I can be the fastest of all of (1)-(4). More generally, how Fortran and PL/I can be faster than C should be exploited quite generally but apparently has not been fully generally.
Why? How can Fortran and PL/I beat assembler and C?
In particular, first, Fortran can easily beat C in subroutines that receive multidimensional arrays as parameters. Here PL/I also can beat C for the same reasons. Second, PL/I beats both Fortran and C in string handling.
How, why, what the heck is going on? In simple terms, first, Fortran gets to compile the multidimensional array handling within the language and C does not: In C, the programmer has to write the array addressing logic (e.g., row major or column major) that looks to the C compiler like just more code where the compiler has to treat that handling as general purpose code; the Fortran compiler KNOWS that the work is array handling and gets to make better uses of registers and to store intermediate results where the Fortran source code can't see them and, thus, take some liberties. That is, in Fortran, the array handling is in the language where the compiler can take liberties where a C compiler has to treat the work as ordinary code and not take the liberties. PL/I has the same advantages. Second, similarly for string handling in PL/I.
In particular, commonly in C and Fortran, string handling is via subroutines that to the linkage editor look like external references that must be linked. That indirection, stack manipulation, register save, restore, etc., PL/I doesn't have to do.
Scientific, engineering, analytical code is awash in multidimensional arrays, and there Fortran and PL/I can beat C. Now nearly all code is awash in string handling, and there PL/I can beat both Fortran and C.
For assembler, the old remark was that in practice an assembler programmer would write his own code that was less well designed and, thus, slower than C, Fortran, and PL/I. Further, the assembler programmer would be tempted to have a large library for arrays and strings that would be external subroutine calls likely with the overhead. Sure, in principle, assembler should win, but in practice writing code good enough to win is usually a bit too much work.
More generally, compiled functionality offered directly by the language can be faster than libraries of external subroutines, methods, etc.
How current is this impression? There continue to be first-rate Fortran compilers that probably win on numerics for exactly the reasons you state, but I would be surprised that PL/I has a toolchain that's kept up with modern architecture.
String handling, on most modern toolchains, mostly goes to inline functions or builtins that the compiler has been (rather pragmatically, if a big disgustingly) made aware of. It's very unlikely that strcpy or memmem is going through external linkage.
My experience is old, but my summary point remains: Language defined and compiler supported functionality usually is faster than libraries of external functions.
The relevance here: C isn't the fastest thing around and can be beaten. Maybe similarly for Rust.
For PL/I and strings, for the IBM versions on S/360 and S/370, the compiler might have used some of the hardware instructions for string handling.
I'd be surprised if in C strcpy, etc., were not still external: They USED to be, and then someone might have written their own version, e.g., for their own versions of strings, and linked to it; for compatibility, strcpy would about have to be an external function call still.
The compiler (off the top of my head this applies to GCC, clang, and MSVC, dont know about ICC) generally understands the C string functions at a fundamental level and will emit optimized code instead of a function call that will use hardware instructions and might know the size of the buffer at compile time. For instance on GCC you have to specify -fno-builtin to say "you don't know what you think you know about c library, actually emit those function calls instead of trying to optimize the idea of those calls."
I get it now: My view of HN just went down to the sewer: We're into the old, bitter, go out back and fight it out, religious computer language wars. And there C is one of the grand sacred subjects:
C is the greatest. The greatest, best ever. The greatest, fastest of all time, never to be improved on. Pure genius. Pure gold. And C programmers are like people who climb Mount Everest barefoot with no tools and the only REAL programmers.
Yup, C religion.
My view, bluntly: C was a toy language for an 8 KB DEC computer, well known to be a toy at the time. It's still a toy language and so primitive that it is a huge waste of effort in computing. There's no excuse for it and hasn't been even from the first day it was written, even for operating system code, since even at the time Multics was written in PL/I. And C is so primitive, in its K&R definition, that for string and array functionality, it is SLOW.
Besides, the idea that we are stuck-o with strcpy, etc. as in the original K&R external library means that we can't implement array bounds checking, reports on memory usage, etc. with an upgraded library of external functions.
My points here are rock solid: Functionality defined in the language and implemented in the compiler can be a lot faster than implementations with libraries of external functions. On strings, C and Fortran are examples. On arrays, C is an example.
If Rust is not a lot faster than K&R C, then I'm surprised at Rust.
All those hero barefoot Mount Everest climbers might notice that they are really tired, their feet hurt, and they very much need some better equipment!
Ah, programming language religious wars!!!!!
Grow up HN; set aside the toys of childhood. Be rational and set aside religious wars.
You appear to be making your points in a way in which no developments from the part 10-20 or so years are intruding. Can you point to a extant PL/I compiler that produces good code on modern architectures? How sure are you that the issues that separated C, Fortran and PL/I historically are even remotely relevant now?
I would imagine inlined functions (and for the brave, interprocedural analysis) renders many of your points moot. Fortran still has an edge on numerics as I understand it, but I don't think it's all that decisive.
The other reason that baking functionality into the language is problematic is that you wind up having a few things that go fast, while you neglect to build facilities that optimize other people's data structures. So you get the world's fastest dense array (say) but your sparse array is resolutely mediocre. Instead of a language packed with special-cases, I would much rather a minimal language with good analyses (many of which can be supported by language design; I think Rust has a good future here to support good analyses by how carefully it tracks lifetimes and how it doesn't fling aliases around with the abandon of C).
> You appear to be making your points in a way in which no developments from the part 10-20 or so years are intruding. Can you point to a extant PL/I compiler that produces good code on modern architectures? How sure are you that the issues that separated C, Fortran and PL/I historically are even remotely relevant now?
It's simple. Again, once again, over again, yet again, one more time, the main point is that functionality in a language definition and implemented in a compiler is faster than that functionality implemented in external functions. Simple. Dirt simple. An old point, and still both true and relevant and not changed in the last "10-20" years.
C is not supposed to have changed from K&R -- that it is backwards compatible was one of its main selling points and main reason to put up with such meager functionality that had programmers digging with a teaspoon.
Fortran and PL/I just illustrate the point. The point is thus illustrated. For this illustration, I can go back to old Fortran 66 and PL/I F level version 4. Still the point is illustrated and remains true.
And for the changes in C, does the language yet have definitions and support for multidimensional arrays and something decent in strings? And what about Malloc and Free -- have they been brought up to date?
I did NOT propose, I am NOT proposing, using Fortran or PL/I now. Instead, again, once again, over again, ..., I just made a point about language design and implementation. That this evidence is old is fully appropriate if we regard C as defined in K&R and not changed since. If people changed it, then it's a different language and likely not fully compatible with the past.
So, this thread was about C: I assumed C hasn't changed so assumed K&R C.
I don't want to use C but have, recently in one situation was essentially forced into it. But after that case, I left C. I'm not using Fortran or PL/I, either.
I'm using Microsoft's .NET version of Visual Basic (VB). Right, laughter, please, except it appears that .NET VB and .NET C# differ essentially only in syntactic sugar (there are translations both directions), and I prefer the VB flavor of that sugar as easier to teach, learn, read, and write than C# which borrowed some of the C syntax that K&R asserted was idiosyncratic and in places really obscure.
For this thread, I was surprised that Rust would not run circles around old, standard K&R C. Soooo, the answer is in part that the C of this thread is not old K&R C. Okay, C has changed and is no longer seriously backwards compatible. Okay. Okay to know, but for a huge list of reasons in my work I'm eager to f'get about C and its digging with a teaspoon.
If the new versions of C do not do well handling multidimensional arrays passed as arguments, then C should still be consider TOO SLOW, and too clumsy.
But, whether or not I use C, and whatever I think of it, the point remains: Functionality in the language is faster than that implemented via external functions.
Of course, this point is important in general when considering extensible languages. Sooo, maybe if could do for syntax as much as has been done for semantics, then we could do better on extensible languages. But then we could extend 1000s of ways, maybe once for each application, and then have to worry about the 1000s of cases of documentation, etc. So, extensible, when do it, has pros and cons.
I see: Discussing language design, especially for sacred C, is heresy in the religious language wars. Forbidden topic. Who'd uh thunk -- HN is full of hyper sensitive and hostile religious bigots.
> C is not supposed to have changed from K&R -- that it is backwards compatible was one of its main selling points and main reason to put up with such meager functionality that had programmers digging with a teaspoon.
C has gained new functionality and features. It just hasn't deprecated the old ones. K&R C will still compile with a modern C compiler, but it won't be as good as modern (C99/C17) C.
I really wasn't interested in the role of time. I was just struck that Rust was not a lot faster than C. Even with strcpy, malloc, free, etc. all compiled, C should still be slow due to essentially no support for arrays with more than one subscript. Thus I would expect that a modern language with good array support would beat C and am surprised that Rust does not.
Array support is now very old stuff; gee, now we'd like balanced binary tree support, maybe AVL trees, maybe like the C++ collection classes, but compiled and not just classes with its too much indirection. And we'd like good multi-threading support. Some of that is in the .NET languages; then they have a shot at beating C. I would expect that any language taken seriously today would beat C, the K&R version, the ANSI version, some recent version, etc.
And I'd like a lot more, e.g., some good semantic guarantees from static code analysis.
BTW in my remark above, have to swap "syntax" and "semantics" -- we have good progress on syntax but less good on the more difficult semantics.
A practical challenge is given some code, say, 100 KLOC, maybe 1 MLOC, have some static analysis that reports some useful information. Then have some transformations that have some useful guaranteed properties. If current languages do not admit such, then try to come up with a new language that does and still is easy enough to use and fast enough.
Gee, I assumed that after all these years we'd get some progress -- that Rust doesn't run circles around C is disappointing.
> I really wasn't interested in the role of time. I was just struck that Rust was not a lot faster than C. Even with strcpy, malloc, free, etc. all compiled, C should still be slow due to essentially no support for arrays with more than one subscript. Thus I would expect that a modern language with good array support would beat C and am surprised that Rust does not.
What do you think "array support" means to the compiler? Fortran has historically been faster just because of the the aliasing rules, but C has the restrict keyword now that brings it up to snuff there. The standard library inlining is more a hack around the fact that the c stdlib is dynamically linked, and that doesn't always make sense for heavily used functions.
I'm not really sure where you got this idea that C is a low bar. For instance here's C meeting or beating Fortran on every benchmark except one (where statistically they're probably tied). https://benchmarksgame-team.pages.debian.net/benchmarksgame/...
Soooo, those C compilers cheat on the language as defined in K&R etc.!!! WOW!!
With that cheating, C gets to catch up with, say, what PL/I was doing with string manipulations in, say, 1969!!!
So, this is the 50th anniversary!! C string handling is right up to date as of 50 years ago!!!
There is still the issue of a C programmer having to calculate array indices that Fortran and PL/I can do back, way back there, 50+ years ago!!!! What was it, Fortran 66 or some such???
> It doesn't "cheat"; the semantics of the C standard library are just as much a part of the language as the rest.
As I read K&R, strcpy, etc., where there, in a library of external functions, as a convenience and optional for the user, and NOT part of the definition of the C language. Your YMMV.
The situation definitely is "cheating": In K&R,
strcpy is clearly, syntax, semantics, original implementation, an external library function. K&R has the definition of the language. One of the most important parts of C is that it doesn't change. It was clear in K&R that strcpy could be implemented by any user in their own external code.
So, a compiler that implements strcpy with its own in-line, not with external names, code is cheating on the language definition.
C as in K&R was designed to run in a DEC computer with 8 KB of main memory. So, the K&R definition of C was primitive.
Since strings in C as in K&R are so primitive, really a disaster, a situation well understood when K&R was written, some programmers might have done a much better string implementation via external function calls. So, given some old C code, they could have linked in their own external calls not mentioned in K&R and for parts of the old code that called strcpy linked in their own version of that function that worked with the new string functionality. All to have been expected.
So, some compiler writers have gotten more performance from C by cheating some on the language and put in compiler options to justify the cheating.
With this cheating, the compiler writers are
making my main point: Functionality defined in the language and implemented in the compiler via in-line code can be faster than functionality implemented in external function calls. That is, the more recent C compiler writers so strongly agreed with this point that they cheated on the language definition to get the gains. This point was made very clearly by IBM as they pointed out that the string functionality in the PL/I language and the IBM compilers was faster than the many Fortran string packages implemented by external functions.
Being faster than K&R style C is easy. So, here I was struck by the point that Rust is not always a LOT faster than C, that is, K&R C. But, maybe Rust IS a lot faster than C with a compiler that does no cheating!
Who said? K&R is pre ANSI C, so pre any standardization.
The concept of the C standard library post-dates ANSI C, so to say that they "changed" the language in that they more closely documented the behavior of the language and the standard library means that, for all intents and purposes, the standard library is part of the language.
Which is exactly the point you are making about FORTRAN and PL/1. That they have standardized functions that perform in a documented manner and so can therefore be optimized by the compiler.
That C compilers understand the defined behavior of strcpy et al means that they are allowed to optimize the implementation.
C/C++ compilers will happily optimize standard library functions. A call to printf that doesn't use any % formatting will usually get converted into a call to puts, for example. And strlen of a string literal is of course replaced with a constant integer, and strcpy will be converted to memcpy if the length is known. And memcpys of known length are of course emitted with appropriate move sequences.
This is a great comment, but it's in the wrong place. The OP is about using Rust/C to compose network and packet handling software; using Fortran there would be completely pointless. There is no easy gain to be found from vectorization in networking.
I was not bringing up vectorization in Fortran. E.g., in Fortran we can have
Real X(20, 30,40), all
Call addall(X, 20, 30,40, all)
and have
Subroutine Addall (Z, L, M, N, Result)
Real Z(L, M, N), All, Result
Integer L, M, N, I, J, K
Sum = 0.0
Do 100 I = 1, L
Do 100 J = 1, M
Do 100 J = 1, N
All = All + Z(I, J, K)
100 Continue
So what is good here is that in the
subroutine the parameter Z
gets its array bounds PASSED
from the calling routine, the
statement
Real Z(L, M, N)
honors those bounds, and the
code then does the indexing
arithmetic (row major or column
major, I don't recall which)
using the bounds passed as
arguments to the parameters
L, M, N. That is, in the subroutine
we can have the array bounds as
parameters, and still Fortran does
the array indexing arithmetic.
Can't do that in C: In C the
programmer has to do array indexing
with their own code in the
subroutine. Thus to the compiler
this code is just ordinary code
and has to be treated as such.
In contrast array indexing Fortran
does is not from ordinary code
thus permitting Fortran to make
better use of registers and,
for example, leaving intermediate
values in registers and not
writing them back to storage
in variables known to the source
code.
Here vectorization has nothing
to do with it.
Bluntly, C doesn't support arrays,
and that's a bummer for the programmer
and also execution speed.
My point is on topic because this thread is about C performance, and
I've brought up the point that
functionality, in this case array
indexing arithmetic, defined in the
language and implemented in the compiler
can be faster than the programmer
implementing the functionality in their
own code, especially, in the case of strings, if that
implementation is via external
routines.
> leaving intermediate values in registers and not writing them back to storage in variables known to the source code
C compilers (with optimization on) don't spill intermediates either.
Your decades of experience are appreciated, but apparently your decades of experience don't cover looking at what optimizing C compilers learned to do in the last 30 years or so.
> Bluntly, C doesn't support arrays, and that's a bummer for the programmer
That's true.
> and also execution speed.
That isn't, or at least you haven't demonstrated it.
Why? How can Fortran and PL/I beat assembler and C?
In particular, first, Fortran can easily beat C in subroutines that receive multidimensional arrays as parameters. Here PL/I also can beat C for the same reasons. Second, PL/I beats both Fortran and C in string handling.
How, why, what the heck is going on? In simple terms, first, Fortran gets to compile the multidimensional array handling within the language and C does not: In C, the programmer has to write the array addressing logic (e.g., row major or column major) that looks to the C compiler like just more code where the compiler has to treat that handling as general purpose code; the Fortran compiler KNOWS that the work is array handling and gets to make better uses of registers and to store intermediate results where the Fortran source code can't see them and, thus, take some liberties. That is, in Fortran, the array handling is in the language where the compiler can take liberties where a C compiler has to treat the work as ordinary code and not take the liberties. PL/I has the same advantages. Second, similarly for string handling in PL/I.
In particular, commonly in C and Fortran, string handling is via subroutines that to the linkage editor look like external references that must be linked. That indirection, stack manipulation, register save, restore, etc., PL/I doesn't have to do.
Scientific, engineering, analytical code is awash in multidimensional arrays, and there Fortran and PL/I can beat C. Now nearly all code is awash in string handling, and there PL/I can beat both Fortran and C.
For assembler, the old remark was that in practice an assembler programmer would write his own code that was less well designed and, thus, slower than C, Fortran, and PL/I. Further, the assembler programmer would be tempted to have a large library for arrays and strings that would be external subroutine calls likely with the overhead. Sure, in principle, assembler should win, but in practice writing code good enough to win is usually a bit too much work.
More generally, compiled functionality offered directly by the language can be faster than libraries of external subroutines, methods, etc.