-- Fran Allen interview, Excerpted from: Peter Seibel. Coders at Work: Reflections on the Craft of Programming
UNIX had very little to do with it, only very few people were lucky enough to have access to UNIX machines but untold 100's of thousands had access to PCs or 8 bit micros. The first time I saw a UNIX machine it was an Acorn 'Unicorn' and it was so far ahead of what I could afford that it might as well not exist.
It was just another programming language fighting for developer eyes.
On Windows and OS/2, although IBM and Microsoft decided to go with C for the underlying low level layers, C++ was the way to go for high level coding, C Set++, MFC. With Borland having Turbo Vision, OWL and VCL.
Macs were Object Pascal territory, and when MPW got C and C++ support, PowerPlant C++ framework was the way to go.
Epoch, BeOS and Symbian were also C++ territory.
MS-DOS games were also adopting C++ via Watcom and its DOS extender.
UNIX and the rise of FOSS, based on UNIX culture, were definitely the only reason.
And IMHO, at that time, before or around C++98, C++ didn't fix a single problem of C, but instead just added a lot of new ones (one could argue that this is still the case even today).
I was getting paid for teaching C++ on commercial training courses in 1990 - the C++ courses were probably the most popular after C and UNIX
> before or around C++98, C++ didn't fix a single problem of C
Of course it did, or why would people like me have transferred wholesale from C to C++?
C was never taught as such, as any student was expected to know it from their C++ classes.
The professor was a great teacher of what all the ways that C++ fixed C's problems, by providing his own data structures for strings, arrays, vectors, linked lists and hash tables, all with bounds checking enabled by default on his implementation.
Other issues that C++ fixed over C was having implicit conversions, a proper way to allocate memory (malloc() with sizeof, really?), ability to ensure valid pointers via references.
> malloc() with sizeof, really?
One tiny macro to solve 20% of all the problems people are whining about.
void _alloc_memory(void **ptr, size_t numElems, size_t elemSize)
{
size_t numBytes = safe_multiply(numElems, elemSize);
void *p = malloc(numBytes);
if (p == NULL)
fatal("OOM!\n");
*ptr = p;
}
#define ALLOC_MEMORY(ptr, numElems) _alloc_memory((ptr), (numElems), sizeof **(ptr))
int *myArray;
ALLOC_MEMORY(&myArray, 25);
I've been using this for years without a problem.Plus all C workarounds for "safe" code tend to fall apart when teams scale above 1 team member, as it keeps being proven by endless industry and academic reports.
Now Android NDK is Fortify enabled, with hardware tagging planned for all new ARM based models.
There is a difference between "explicit code" (which is mostly a good thing) and "boilerplate". It's saying what you mean vs saying what the platform requires you to say (or repeat, involuntarily). C definitely leads to the former, but not to the latter.
That said. It's a 1 line macro! You are being ridiculous. The amount of insanity we have to go through in so many other languages constantly, not just for setting a good base, is something completely different. It's not measured in handfuls of lines, but in number of hairs pulled out.
Compare:
#define ALLOC_MEMORY(ptr, numElems) _alloc_memory((ptr), (numElems), sizeof **(ptr))
https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/include/bits/stl_vector.h
(Not a fair comparison, but I think it makes a very good point)> for something that even Algol supports properly.
Probably with an allocation builtin, not allowing for custom allocators?
> supports properly.
There is nothing in this simple macro that isn't "proper". There are no ways it can break (although I still feel it would be nice to have expressions macros, not only token macros). The only requirement is you write it yourself. C in general doesn't like to give you canned things. It has made such mistakes in the past (see large parts of libc) and has actually learned from it.
> Plus all C workarounds for "safe" code tend to fall apart when teams scale above 1 team member, as it keeps being proven by endless industry and academic reports.
You could absolutely implement C with managed memory. It just wouldn't be a good idea. Use other languages if you want these tradeoffs.
Some of the most massive codebases in the world are C. (Often disguised as "C++ by experienced devs"). They are maintainable, protected investments, many decades old, and still in working order, precisely because a minimalistic language approach leads to modular APIs. It scales very well, and the "problem" might be mostly that the defect rate per line doesn't go down as the lines go up.
But the best feature of these codebases is that they exist, because C enables independent development of subsystems much better than the intertwingled messes and dead ends that most "statically-systematic" approaches lead to on non-trivial scales.
It's a huge boon that I don't have to think about rewriting interfaces using multiple inheritance or virtual inheritance or template insanity or SFINAE or unique_ptr or move semantics or rvalue references or static assertions or compile time evaluation, or the next fad around the corner, every 5 years.
ISO C doesn't offer full control over anything beyond the abstract memory model of the standard.
We all know that C is not without flaws. I would use other languages if there were good alternatives. For example, I liked some parts of Delphi, but it has too many show stoppers. For example, all local variables still have to be declared in the variables section before the function body, right? And are we still required to make type aliases to use pointer types in important places, such as function signatures?
C is special in that it is the only language that I've known that has a minimalistic attitude, resulting in a shitty language (every language is shit!) whose problems we can actually work around in practice.
Delphi is not Go, you can declare variables at the point of use nowadays.
Just like you can declare pointer types in function signatures, which would fail my code review, as those things tend to get out of hand.
Minimalist attitude leads to write only code bases, where it is impossible to maintain on long term projects with regular rotating team members, as it usually happens in most multinationals.
A workaround done today is a security exploit waiting to happen tomorrow.
Oh, it seems they introduced it in 2018, some time after I quit my 6-months Delphi stint. http://blog.marcocantu.com/blog/2018-october-inline-variable...
So, given the age of Delphi (and Pascal!), Go still has plenty of time to be quicker. Not sure what's missing from it, though.
Of course, I'm sure you knew all of that, and could have just mentioned it. But maybe you just want to convince people of unrealistic propositions, and claim that some obscure technologies were more practical than they really are.
> Just like C used to declare variables until C99.
1. C99 was 20 years ago, 19 years before Delphi.
2. What you say is wrong. You could declare variables at the start of any block since forever (I think it's standardized in C89).
3. What matters is compilers in practice, and I'm pretty sure they allowed you to declare variables anywhere, and also "for (int i..." (which is C99) since forever (as an extension).
> write only code bases
such as C++ code bases?
> regular rotating team members
I've just never seen that not becoming a mess
> A workaround done today is a security exploit waiting to happen tomorrow.
I'm still waiting for my code review. https://news.ycombinator.com/item?id=21290314 . For a start, where are my terrible workarounds?
Maybe it's just me, but I haven't seen this being a major problem in C. Most optimizations are local and fairly easy to reason about. C++ is a whole different story.
I might be in the minority, but to me, C is as high-level as we should go, for many many problems. If you really care about registers and SIMD stuff, then your concerns are architecture specific, and that's not really what C does well. What C does is mostly abstracting registers. Why blame it for that? The few places where you need SIMD, well, just insert architecture specific code there.
Is there a way to write portable code that can be better optimized?
That quote was true for some time but running into the limits of CPU clock gains for single threaded tasks all those optimizations are more than valid once more, and as you've no doubt noticed there is a veritable run on multi-core solutions embedded deeply in the languages, of which this article illustrates one special case using a co-processor.
C compilers are only speed monsters thanks to almost 50 years of research in optimizing C and C++ compiler backends.
Ok.
> C compilers are only speed monsters thanks to almost 50 years of research in optimizing C and C++ compiler backends.
Lots of those optimizations apply in one form or another to other languages as well.
And... so which is it? Are C compilers fast and the thing to beat or did that research go nowhere?
C is the speed benchmark because 'beating C' is what will get you a foot in the door. Being 'slower than C' is going to get your pet language booted out the door because:
- companies tend to compete on speed of execution
- the speed of the compiler itself is a major factor in turnaround time for the typical edit-compile-test cycle
- nobody cares about security until they've been bitten hard.
This is all very frustrating but it seems to - in my experience - accurately reflect priorities in lots of corporations. It is up to us to change that.
See the title: it is about the speed of execution, and it uses one technology 'GPU' to challenge another 'CPU' and yet the accent is on which language was used.
For CPUs, I'd like to introduce you to my good friend Fortran.
That's what I'm missing from these benchmarks - how does it fare against a handwritten, competent implementation in those languages?
That's fair, but it tells you nothing about the performance gap introduced by these high-level abstractions.
Futhark does not outperform expertly hand-optimised GPU code, but most of the GPU code found in the wild is hardly expertly hand-optimised. Futhark comes out on top surprisingly often, but can be solidly beaten for complex algorithms or clever implementations. See figure 8 in this paper for examples: https://futhark-lang.org/publications/ppopp19.pdf
If your application is better suited for GPUs of course it'll be faster than C on the cpu. GPU mining and AI training are done on the GPU for a reason, and they of course beat "C" on the cpu.
Fair enough about apples to oranges, but it was really distasteful to me that the top comment on this post was about how C is “unbeatable”, when the article clearly showed that Futhark was faster. And since we’re at the dawn of a new age of parallel computing, statements like that are absurd in general.
Barely...
It also mmaps in a whole 100 meg file. :P wc is not optimized to count as fast as possible, resource use be damned.
However, in practice, X-on-GPU where X is not Futhark is rare, because GPU programming is notoriously difficult and time-consuming. Futhark's purpose is to make high-performance data-parallel programming more accessible, even if you could potentially write a faster program yourself.
There are no empirical measurements that I know of, but I would not be surprised if it is a hundred times faster to write a Futhark program than the corresponding OpenCL program. CUDA fares a little better, but not by much. So even if your hand-written program might be twice as fast as Futhark, do you really have the time to write it in the first place? And if you later want to make a small change to its logic (say, adding another parallel loop on top), you may need to rework all of your optimisations from scratch.
> ispc compiles a C-based SPMD programming language to run on the SIMD units of CPUs and the Intel Xeon Phi™ architecture; it frequently provides a 3x or more speedup on CPUs with 4-wide vector SSE units and 5x-6x on CPUs with 8-wide AVX vector units, without any of the difficulty of writing intrinsics code. Parallelization across multiple cores is also supported by ispc, making it possible to write programs that achieve performance improvement that scales by both number of cores and vector unit size.
There is also an interesting story of its development (full with office politics drama :-P) written by its -then- main developer: https://pharr.org/matt/blog/2018/04/18/ispc-origins.html
Fortran scares people because it is often identified with FORTRAN77. I find very unfortunate that the concept of F (a modern simplified Fortran without the legacy features [1]) never took off, although I understand the reasons.