2 ms·
As you probably appreciate, there's a lot of a difference between just being able to write assembler, and being able to write optimized assembler that will beat
by HarHarVeryFunny 3y ago
As you probably appreciate, there's a lot of a difference between just being able to write assembler, and being able to write optimized assembler that will beat a modern optimizing compiler (else what's the point, other than fun, unless you are the one writing the compiler, which seems to be your interest).
One of the issues with modern processors (wasn't true back in the day with the old 8-bitters) is that the processor is so much faster than memory access that this needs to be taken into consideration when writing optimized code. Instruction timings (number of clock cycles) for memory access are going to vary a lot depending on where the data is being held - in cache or in main memory. Writing optimized code (high level as well as assembler) therefore becomes not just a matter of making the code itself as minimal and fast as possible, but also organizing the program's data access to operate out of cache as much as possible and minimize main memory access. The key is to be sensitive to the layout of your data in memory, and try to have your inner loops/code access nearby (same cache line) data rather than hopping about all over the place. e.g. If you have a 2-D array that's laid out in memory row by row (vs col by col), then you want to access it that way too (work on rows) to take advantage of cache.
I used to write a lot of 8-bit assembler back in the day (as well as more recently for some retro-computing fun), but never x86, so don't have any specific resources to share. Once you've learned the basics of the instruction set, a good point to start might be to take some simple functions and compile to assembler both with and without optimization enabled - and try coding the same function yourself in assembler to see if you can beat the compiler. Search for "x86 tricks" type of resources too - the things that other assembly programmers have learnt how to optimize use of the instruction set and write fast and compact code.
Note that cache considerations apply to code as well as data, so you want your code to be compact (fit as much of your inner loops into cache as possible), and also to branch as little as possible, for two reasons. First, you want to take advantage of cache by executing consecutive instructions as far as possible, and second branching kills the pipelining performance (you are throwing away work already done) of modern processors, even though they try to mitigate this with branch prediction.