I'd be interested to see more research on this. I would hope that modern compilers can generate better optimised code than hand assembly for most cases by now.
Does anyone have any pointers to research on this topic?
In general, compiler-generated assembly is on par with naieve hand-written assembly. It's not going to beat something that anyone put any real time into, but if time to market matters at all, you're not going to be able to put large amounts of time into hand-coding assembly.
A good, clever human can generally beat a compiler.
There's a reason that the core of nearly any high performance multimedia, graphics, or encryption library is written in assembly, or compiler intrinsics (which are pretty much assembly instructions written in C syntax)
Agreed. I have used C almost every day for the last 25 years... And not only is the C more maintainable, it is more easily ported to new CPU architectures than assembly (complete rewrites in that case) -- and yes, it is a fast, low overhead language -- especially moreso when using the CPU vendors compilers over GCC since they already know the best way to optimize for their own architecture.
For decades people have said that compiler's code generation is at par... and it slowly has become more true. I read a lot of disassembly listings and I'm often pleasantly surprised at how clever some of the code is. However, there's also plenty of times where I see plainly wasted instructions as well.
That's not the real reason that assembly is often not worth it, even in situations where it once was. The real culprit is that for a long time CPUs were outpacing memory in speed gains, so a cache miss became more and more expensive relative to the clock speed.
This meant that more and more micro-optimization work has gotten focused on cache behavior. C gives you just as much control over how your data structures are stored as assembly would. Maybe you'll need to define some prefetch() macros depending on your compiler, but that's about it.
There are certainly some remaining cases where you really want to control things at a register-allocation level (encryption, codecs, fancy floating-point things) However, most projects are better off focusing on improving their memory behavior rather than trying to get to that cache miss in 80 instead of 82 cycles.
The other huge shift in performance-oriented computing is, of course, the availability of more and more CPU cores. Again, lots of work to do but assembly doesn't give you any advantage at all.
So even if you're good enough at writing assembly to beat the compiler (and most people aren't) you probably should have been spending your optimization effort on other things.
Yes, compiled C or C++ is almost always better. This is a major reason why the Microsoft compilers no longer support inline assembly code on the 64-bit architectures. (you can still link in assembled binaries of course)
This brings to mind another reason why the statement "Wrong: use Assembler Language" is so goofy. You're never going to use assembly language. You're going to use 99.9% C or C++ and a bit of assembly language. "Use C if you want something to go as fast as possible" is still basically correct.
Granted, it's a niche case, though an important one -- using a switch statement for bytecode dispatch leads to poor branch prediction in VMs, and better alternatives require either non-standard extensions (http://eli.thegreenplace.net/2012/07/12/computed-goto-for-ef...) or assembler.
If you're really concerned with performance when writing C, try to avoid painting yourself into a corner. Profile. Consider custom memory allocators, if much of your time is spent creating short-lived or equal-sized objects. In general, look at the tricks the really hardcore game devs do in C++.
Also, use valgrind, because you'll probably cut too many corners. ;)
I thought people stopped writing Assembly a while ago but I recently interviewed at Apple and I interviewed with one group that deals with optimizations for vector mathematics using special hardware on the A4/A5 chips and he said they still write some code in Assembly because for performance reasons.
I hope one of the parts deals with people's aversion to C declarations. The declaration syntax allows vastly complicated data definition, and the fact that declaration looks like use simplifies things immensely.
(I also have a personal style quibble -- lots of people seem to think that putting spaces around every lexical element clarifies things; I think it just makes gassy code.)
As a fan of C, I have to disagree. To quote OS X's `man signal`:
void (*signal(int sig, void (*func)(int)))(int);
or in the equivalent but easier to read typedef'd version:
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);
In, say, Go, this would be
func signal(sig int, newFunc func(int)) func(int)
which imo is cleaner (it's clear that the second argument and return value have the same type, without having to use a typedef) without sacrificing power.
> C is an easy language to write code in. Especially if you are competent in some other block structured language - such as Javascript, Python, Ruby and (maybe) Java.
I disagree. You can be competent in Python,Ruby and Java and still have no clue about how memory works internally, a strong requirement while writing C. C is easy to learn but takes time to master to reach a point where you are not shooting yourself in the head left and right.
> Pointers are bad only if you're an idiot.
You don't have to be an idiot to make a pointer mistake, off by one error, overflow a buffer etc... People that wrote nginx, apache, bind, linux kernel, and freebsd are not idiots yet such mistakes happen.
But what about buffer overflows? Buffers overflow when a fixed size buffer is allocated, some data is copied to the buffer and the idiot who wrote the code didn't check to see if the data was too big for the buffer!
Researchers have found memory corruption flaws in both Daniel J. Bernstein and Wietse Venema's code. You want to try this "de-myth-ification" thing again?
You can be a phd mathematician and still do arithmetic wrong. And for my next trick I'll argue the semantics and usage of "idiot" in the quoted passage (exercise left for reader).
You can also apparently be two world-class experts at secure C programming and do this arithmetic wrong. But don't worry, you won't make those same mistakes! Only idiots make those mistakes!
Here's a chance for you to improve your standpoint; I'd recommend taking two minutes aside, typing both names into Google and reading a bit about both people first. You might learn something ;)
They are both smart mathematicians, but if you trust user code and don't check for conditions that are that well known, or have set up some automatic system to catch these issues (ala GCCs automatic warning if you use gets()) then yes, you are an idiot.
If you get owned by something that isn't well known, by a bug in the CPU, by a corrupt compiler, by a cross site script attack ten years ago, fine, but not something that everybody should know is dangerous.
If you aren't deliberately trying to miss the point, TFA claimed "only idiots make buffer overflow error" and the OP is implying it's not just the idiots, but very smart people(look up the names) who make buffer overflow errors as well.
> It is a simple rule. Never trust user input, the issue has been known for decades and everybody should be aware of it by now.
It's a simple rule. Don't write buggy code. The issue has been known for decades and everybody should be aware of it by now.
/s
I am trying to find the buffer overflow in DJB's code. I am more interested in the fact if it was a simple error(give too long an input and it goes kaboom), or a complicated one. My intuition is it is the complicated kind. DJB is known to actively use his own implementation of stdlib and strings which are coded from scratch to be secure than the standard counterparts.
Anyone that thinks that Java byte code is just interpreted at this point clearly has no right to talk about such things. This is about the third article by C programmers in the last month where they believe that Java (and C#) are not ultimately running compiled machine code and are not interpreted. Why has this misconception lasted so long? The first JITs were released last century...
> C generates compiled code. Code which is very efficient, but not as efficient as hand coded machine instructions.
...written by a perfect programmer.
> C is an easy language to write code in.
I think the word he was looking for was "simple". The field of land mines known as "undefined behaviour" disqualifies C as being an easy language.
> So you have to use pointers - which are nothing more than variables which contain addresses.
Except that they have their own semantics and syntax as well as their own fun set of undefined behaviour.
> This means you can define local variables which shadow external variables quite freely. This makes you code easier to read and safer to develop ...
Because everyone loves to keep track of which version of the 'count' variable you're referring to! (Seriously, there is almost never a reason to define the same variable name twice in the same function.)
> imperative: C statements are tasks to be executed in sequence. Hence imperative.
The word imperative doesn't imply a sequence...
> C doesn't support objects
Pretty much anything that's not a function is considered to be an object. C doesn't support polymorphic classes. (Well... let's not get into that!)
Overall, this might be useful to the author if he finds that he can't remember the details of the language for very long. Anyone not already familiar with the concepts he's talking about will be lost at best, or damaged in the average case.
Pretty much anything that's not a function is considered to be an object.
In general, the definition of object is incredibly wishy-washy and the word really ought to be retired, but as it stands, I don't think many people would agree that just any old piece of non-function data is an object. Different languages/people/formal systems have different definitions; for example, in Python, functions are themselves objects, while Java makes a clear distinction between objects and non-object primitive types like ints.
Mind you, this is as much a criticism of the original article's usage as yours; from a certain point of view (e.g. C++'s), C of course supports 'objects' inasmuch as you can store function pointers in structs and consequently have run-time selection of functions, after a fashion. From another point of view (e.g. Alan Kay's), even C++ does not have objects, because it lacks true message passing. The real criticism is that the term 'object' is so vague as to be essentially meaningless without clear context.
36 comments
[ 4.0 ms ] story [ 94.6 ms ] threadI'd be interested to see more research on this. I would hope that modern compilers can generate better optimised code than hand assembly for most cases by now.
Does anyone have any pointers to research on this topic?
A good, clever human can generally beat a compiler.
There's a reason that the core of nearly any high performance multimedia, graphics, or encryption library is written in assembly, or compiler intrinsics (which are pretty much assembly instructions written in C syntax)
That's not the real reason that assembly is often not worth it, even in situations where it once was. The real culprit is that for a long time CPUs were outpacing memory in speed gains, so a cache miss became more and more expensive relative to the clock speed.
This meant that more and more micro-optimization work has gotten focused on cache behavior. C gives you just as much control over how your data structures are stored as assembly would. Maybe you'll need to define some prefetch() macros depending on your compiler, but that's about it.
There are certainly some remaining cases where you really want to control things at a register-allocation level (encryption, codecs, fancy floating-point things) However, most projects are better off focusing on improving their memory behavior rather than trying to get to that cache miss in 80 instead of 82 cycles.
The other huge shift in performance-oriented computing is, of course, the availability of more and more CPU cores. Again, lots of work to do but assembly doesn't give you any advantage at all.
So even if you're good enough at writing assembly to beat the compiler (and most people aren't) you probably should have been spending your optimization effort on other things.
This brings to mind another reason why the statement "Wrong: use Assembler Language" is so goofy. You're never going to use assembly language. You're going to use 99.9% C or C++ and a bit of assembly language. "Use C if you want something to go as fast as possible" is still basically correct.
Granted, it's a niche case, though an important one -- using a switch statement for bytecode dispatch leads to poor branch prediction in VMs, and better alternatives require either non-standard extensions (http://eli.thegreenplace.net/2012/07/12/computed-goto-for-ef...) or assembler.
If you're really concerned with performance when writing C, try to avoid painting yourself into a corner. Profile. Consider custom memory allocators, if much of your time is spent creating short-lived or equal-sized objects. In general, look at the tricks the really hardcore game devs do in C++.
Also, use valgrind, because you'll probably cut too many corners. ;)
(I also have a personal style quibble -- lots of people seem to think that putting spaces around every lexical element clarifies things; I think it just makes gassy code.)
Of course, this is not quite true; large parts of the program are JITted, and the result is sometimes faster than C.
I disagree. You can be competent in Python,Ruby and Java and still have no clue about how memory works internally, a strong requirement while writing C. C is easy to learn but takes time to master to reach a point where you are not shooting yourself in the head left and right.
> Pointers are bad only if you're an idiot.
You don't have to be an idiot to make a pointer mistake, off by one error, overflow a buffer etc... People that wrote nginx, apache, bind, linux kernel, and freebsd are not idiots yet such mistakes happen.
Researchers have found memory corruption flaws in both Daniel J. Bernstein and Wietse Venema's code. You want to try this "de-myth-ification" thing again?
It is a simple rule. Never trust user input, the issue has been known for decades and everybody should be aware of it by now.
Here's a chance for you to improve your standpoint; I'd recommend taking two minutes aside, typing both names into Google and reading a bit about both people first. You might learn something ;)
If you get owned by something that isn't well known, by a bug in the CPU, by a corrupt compiler, by a cross site script attack ten years ago, fine, but not something that everybody should know is dangerous.
If you aren't deliberately trying to miss the point, TFA claimed "only idiots make buffer overflow error" and the OP is implying it's not just the idiots, but very smart people(look up the names) who make buffer overflow errors as well.
> It is a simple rule. Never trust user input, the issue has been known for decades and everybody should be aware of it by now.
It's a simple rule. Don't write buggy code. The issue has been known for decades and everybody should be aware of it by now.
/s
I am trying to find the buffer overflow in DJB's code. I am more interested in the fact if it was a simple error(give too long an input and it goes kaboom), or a complicated one. My intuition is it is the complicated kind. DJB is known to actively use his own implementation of stdlib and strings which are coded from scratch to be secure than the standard counterparts.
...written by a perfect programmer.
> C is an easy language to write code in.
I think the word he was looking for was "simple". The field of land mines known as "undefined behaviour" disqualifies C as being an easy language.
> So you have to use pointers - which are nothing more than variables which contain addresses.
Except that they have their own semantics and syntax as well as their own fun set of undefined behaviour.
> This means you can define local variables which shadow external variables quite freely. This makes you code easier to read and safer to develop ...
Because everyone loves to keep track of which version of the 'count' variable you're referring to! (Seriously, there is almost never a reason to define the same variable name twice in the same function.)
> imperative: C statements are tasks to be executed in sequence. Hence imperative.
The word imperative doesn't imply a sequence...
> C doesn't support objects
Pretty much anything that's not a function is considered to be an object. C doesn't support polymorphic classes. (Well... let's not get into that!)
Overall, this might be useful to the author if he finds that he can't remember the details of the language for very long. Anyone not already familiar with the concepts he's talking about will be lost at best, or damaged in the average case.
Mind you, this is as much a criticism of the original article's usage as yours; from a certain point of view (e.g. C++'s), C of course supports 'objects' inasmuch as you can store function pointers in structs and consequently have run-time selection of functions, after a fashion. From another point of view (e.g. Alan Kay's), even C++ does not have objects, because it lacks true message passing. The real criticism is that the term 'object' is so vague as to be essentially meaningless without clear context.
Wrong: use Assembler Language.
That's ridiculous. I could say "Wrong: use machine code." and go on to reach "Wrong: use wire and soldering iron." and go on and on even more.