views:

228

answers:

5

Hi,

Sometimes, mainly for optimization purposes, very simple operations are implemented as complicated and clumsy code.

One example is this integer initialization function:

void assign( int* arg )
{
    __asm__ __volatile__ ( "mov %%eax, %0" : "=m" (*arg));
}

Then:

int a;
assign ( &a );

But actually I don't understand why is it written in this way...

Have you seen any example with real reasons to do so?

A: 

Let compilers optimize for you. There's no possible way this kind of "optimization" will ever help anything... ever!

colithium
+1 for being a good sport, and getting straight to the point. :-)
+10  A: 

In the case of your example, I think it is a result of the fallacious assumption that writing code in assembly is automatically faster.

The problem is that the person who wrote this didn't understand WHY assembly can sometimes run faster. That is, you know more than the compiler what you are trying to do and can sometimes use this knowledge to write code at a lower level that is more performant based on not having to make assumptions that the compiler will.

In the case of a simple variable assignment, I seriously doubt that holds true and the code is likely to perform slower because it has the additional overhead of managing the assign function on the stack. Mind you, it won't be noticeably slower, the main cost here is code that is less readable and maintainable.

This is a textbook example of why you shouldn't implement optimizations without understanding WHY it is an optimization.

JohnFx
+1 for having more patience than me
colithium
Good overall writeup, but missing the most important use of asm - using CPU instructions that C++ doesn't have notation to express. There is no instruction to do highly parallel operations in C++, nor atomic operations, memory cache control etc.. Unless you're writing an incredibly high performance program (e.g. real time HD video for notebooks, real-time derivatives pricing) then it's only such otherwise unreachable instructions that you're likely to use asm for, and if you're lucky the environment will have most of those functions available in non-standard support headers.
+1 for bringing back memories of counting t-states in the days of my youth. Quite right about the assumption that the assembly is always faster is fallacious. My guess is that for the example given it could well be slower.
Shane MacLaughlin
@user433534: thats not true at all, maybe long ago, but there days almost everything is implement as a compiler intrinsic(in most new/up-to-data compilers), parallelism is tackeled with openmp, atomic ops with Interlocked* etc. The real problem is that register allocation can be really poor, or certain really processor specific stuff is not in intrinsic form yet(like say some amd string op instructions, for instance)
Necrolis
+6  A: 

It seems that the assembly code intent was to ensure that the assignment to the *arg int location will be done every time - preventing (on purpose) any optimization from the compiler in this regard.

Usually the volatile keyword is used in C++ (and C...) to tell the compiler that this value should not be kept in a register (for instance) and reused from that register (optimization in order to get the value faster) as it can be changed asynchronously (by an external module, an assembly program, an interruption etc...).

For instance, in a function

  int a = 36;
  g(a);
  a = 21;
  f(a);

in this case the compiler knows that the variable a is local to the function and is not modified outside the function (a pointer on a is not provided to any call for instance). It may use a processor register to store and use the a variable.

In conclusion, that ASM instruction seems to be injected to the C++ code in order not to perform some optimizations on that variable.

ring0
Good point - often you find things in code that were written to address a missing language capability (or missing in that compiler) which has since been rectified. Depending on the age of the code @rursw1 is looking through, this could be what happened.
Kate Gregory
Actually many compilers will optimize the variable "a" completely away and simply call g(36); f(21);
Larry Osterman
@Larry: if they do that, you could just as well say that they've optimised `a` to be stored in the same location (be it stack or register) as the first integer parameter in the calling convention ;-)
Steve Jessop
+1 and its a good point if the code was written by a good programmer. But then I'd also expect a good programmer to say why she/he is resorting to assembler in the first place. Without this commentry alarm bells would start ringing, and my feeling is the code in the example is bogus.
Shane MacLaughlin
@Kate, looking at the age of the code is clever. There's a good chance that if it did once have a legitimate reason for being written that way it no longer does so.
Shane MacLaughlin
@Shane, agreed, a comment is necessary in that case, or people may follow some advices from this thread and inject a bug that will be hard to detect and fix later on ;-)
ring0
A: 

The example you give seems flawed, in that the assign() function is liable to be slower than directly assigning the variable, reason being that calling a function with arguments involves stack usage, whereas just saying int a = x is liable to compile to efficient code without needing the stack.

The only times I have benefited from using assembler is by hand optimising the assembler output produced by the compiler, and that was in the days where processor speeds were often in the single megahertz range. Algorithmic optimisation tends to give a better return on investment as you can gain orders of magnitudes in improvement rather than small multiples. As others have already said, the only other times you go to assembler is if the compiler or language doesn't do something you need to do. With C and C++ this is very rarely the case any more.

It could well be someone showing off that they know how to write some trivial assembler code, making the next programmers job more difficult, and possibly as a half assed measure to protect their own job. For the example given, the code is confusing, possibly slower than native C, less portable, and should probably be removed. Certainly if I see any inline assmebler in any modern C code, I'd expect copious comments explaining why it is absolutely necessary.

Shane MacLaughlin
+1  A: 

While there are several reasonable justifications for writing something in assembly, in my experience those are uncommonly the actual reason. Where I've been able to study the rationale, they boil down to:

Age: The code was written so long ago that it was the most reasonable option for dealing with compilers of the era. Typically, before about 1990 can be justified, IMHO.

Control freak: Some programmers have trust issues with the compiler, but aren't inclined to investigate its actual behavior.

Misunderstanding: A surprisingly widespread and persistent myth is that anything written in assembly language inherently results in more efficient code than writing in a "clumsy" compiler—what with all it's mysterious function entry/exit code, etc. Certainly a few compilers deserved this reputation

To be "cool": When time and money are not factors, what better way to strut a programmer's significantly elevated hormone levels than some macho, preferably inscrutable, assembly language?

wallyk
Regarding your 2nd point, I once used the imagecraft compiler for HC12 at an internship where I was making things for non-technical supervisors. The linker didn't work (even for 1-file programs); I had to write a poor-man's version of one to put my variables in the proper locations and to put code in the right places. I finaly quit using the compiler when I discovered a logical condition test that it could not compile correctly. I couldn't afford another compiler and didn't have time to get the GCC chain running, so I wrote about 2500 lines of assembly to do a simple command/control thing.
San Jacinto