I'm not sure that operating systems are so suited to ASM optimization (in the sense that you may not reap so many benefits). Maybe one could optimize for size (so you can make super tiny OS) ?
In Java, that might involve adding more classes and abstractions to make things conceptually simpler. In C, that might involve using structs to keep relevant data together. In C++, it might involve using a std::set to keep a list of 3 constants, Etc.
It turns out that when you have to write the asm by hand and you see that double pointer dereference is a pain, you avoid data structures with double dereferences (as C data structures often end up with). You avoid classes and abstractions (as are common in Java), because they involve a lot of boilerplate. You use compile time constants for that set of three things rather than any complex hashing that C++ would do, etc.
There are lots of small things like this, and it turns out the effect adds up significantly.
I strongly disagree :-)
My experience is that optimizing assembler leads to reorganize your code or even your algorithm into a form that you'll CPU will be most efficient at executing which almost always translate to unbelievably intricate, super hard to modify code. I've done stuff on 6502, 80x86, MMX and the assembly parts, optimized for speed and ended up with impossibly tricky code. Worst, sometimes I have to adapt my data structures to allow optimization, which becomes even tougher. So, I prefer to leave assembly code at the "onyl if necessary" level
But now, to be honest, optimizing assembly code is incredibly satisfying to me :-) I've got the feeling to use a CPU to its maximum capacity. Also, using assembly comes after thorough algorithmic study. So once I'm at the ASM level, I've maxed out my own capabilities ! How happy me !
So if you have the chance to do that, just give it a try !
I'd assume that when you maintain larger codebases (i.e. full OS and application suite), that you start writing practical and maintainable code instead.
But even so, I'd say that ASM doesn't help. Clumsy code in ASM is worse than clumsy code in high-level language :-)
It's not subliminal. It's packing a backpack that someone else has to carry, vs packing a backpack that _you're_ going to carry.
I do not know, to be honest.
I was just pointing that multimedia and game engines can get a lot of helps on ASM by hand.
There are some SIMD constructs that you can efficiently express in C by writing intrinsics, but that isn't substantially different from writing ASM.
Then there are some trivial cases where a loop can be unrolled and packed into SIMD instructions automagically, which retains readability, but greatly limits what you can write. You'll need to read the generated machine code to make sure you didn't mess up.
The benefit of just writing ASM is to not have such a translation layer between you and the processor. That same code that you carefully wrote to make it through the optimization passes for one compiler will likely get messed up in another compiler.
Frankly, no.
Improving the compiler could get a few dozen of %, which is huge already. But 300%+, no.
Maybe one could optimize for size (so you can make super tiny OS) ?
Yes, and remember... optimizing for code size is optimizing for speed. =)Smaller code and data = more CPU cache hits, which are orders of magnitude faster than fetching from RAM. So even if "all" you do is make code smaller, you can get more speed...
Not always. I think loop-unrolling is a common perf-optimisation technique. Even if the perf-gain is positive in this case, I highly suspect that it doesn't outweigh the cost of maintaining asm code(vs C/other higher level code).
edit: formatting
Now we have CPUs with 256KB instruction cache and up. You can compile your whole OS at insane optimizations.
That's what created multi-kilobyte memcpy() implementations, which barely beat REP MOVSB but cause huge icache bloat.
Until the day the compiler can smartly trim down all the unity framework to just 512 bytes because it notices I'm not using most of it, hand coding in asm will always work out smaller and faster, if one puts in enough effort.
If you compile hello world in C, it's not going to be very big either.
> if one puts in enough effort
"enough effort" is a cheat. I can replace almost any program with a smaller javascript version, if I put in "enough effort". It's not a reasonable way to compare anything.
You should actually try that, and compare sizes with the asm hello world.
Hello world in C becomes anywhere from 120KB to 1.2MB even if just using puts. Even setting the entry point to a function with no arguments doesn't help.
A tiny hello world ends up needing to ignore the standard library and use a console output function from the OS.
If you go to an embedded point of view where there is nothing already supplied, you can get a hello world, even with a basic printf, down to that size.
It is an order of magnitude bigger than the assembly version.
It prints (via write syscall) "hello world.\n" and then calls exit(0).
Assembled with fasm, linked with ld -n, then stripped.
The difference is between just using asm, or going out of the way to try to coerce a C toolchain to minimize the bloat.
When I tell it to emit the assembly with "-S" I get 28 lines of code when I have a feeling assembly is much shorter.
The answer and parent were in no way only talking about OS.
I would expect a much smaller gap as AVX2 is also available through intrinsics on C ?!
One can, for example, write:
typedef int v4si __attribute__ ((vector_size (16)));
v4si a, b, c;
[…]
b += 3; // add 3 to each of b’s elements
c = a + b; // pairwise addition
Using this gives up some control over assembly; it doesn’t guarantee vector instructions get used, but it makes it easier for the compiler to generate them.Also, I’m fairly sure there are vector instructions not covered by this extension.
That's trivial in ASM I believe, but don't know how well it's possible in C/C++, if at all.
https://software.intel.com/sites/landingpage/IntrinsicsGuide...
You can also try to make a union whose one field is a __m64, but beware not to trigger UB conditions.
In fact, any scatter-gather operations were non-existent in Halide.
From what I remember these operations were introduced at some point, but we moved away from Halide.
Also, it was not quite simple to transform a loop that draws sprites over the entire image into a loop that draw sprites over part of image and draws many parts of image in parallel (change nesting). Hand-written CUDA version of the algorithm ended up with exactly that.
Thus, if you need some partial-derivatives-numerical-kernel, Halide is good for you. If you are working on the video decoding, Halide is not that good for you. If you are working on video encoding, Halide will be more of a nuisance than a helping hand (early exits from loops, computable access ranges, etc).
Do you think it would have worked to organize tiles and threads outside of halide and use halide for isolated parts that are already organized into arrays?
On the suggestion in your second part: why use Halide then? Should it be responsibility of Halide to work out the best loop nesting and best use of threads?
Again, Halide was put aside and we used CUDA for final version, exactly because of inability of Halide to do good work in our case.
I don't know about "should", but it seems to me that it would still be valuable, even if working out the threading and organization.of the data into an array.
> Again, Halide was put aside and we used CUDA for final version, exactly because of inability of Halide to do good work in our case
I didn't say anything about that. I'm not sure why you are restating it.
> transform a series of angle-amplitude pairs (radioastronomy) to the image
I wonder how long it will take for compilers close that gap. I assume compilers will eventually produce assembly code that outperforms anything written by human.
Be curious to know what you're thinking of here.
dav1d, the open source AV1 decoder, has now more asm than C code. It's one of the most recent open source projects with significant asm work ongoing.
The asm version outperforms the C version (full optimizations enabled) by 4,5x on AVX2.
We have similar results in SSSE3 (3,5x) and ARM64 (4x).
We're not talking about a few percents, we're talking about multiple times faster.
And AV1 is a standard, so there are no algo shortcuts that can be made: it is either compliant or it is not.
Sure, in most cases, it is not needed to write asm; but there are cases, notably for multimedia, where writing asm by hand is a lot faster, and that includes codecs and game engines.