6 ms·
Neither is true. Well, the first might be true for you in your mind, but doesn't change the fact that C is a high level language modelling an abstract system, a
by Annatar 7y ago
Neither is true. Well, the first might be true for you in your mind, but doesn't change the fact that C is a high level language modelling an abstract system, and providing that model through its standard libraries (which is what makes it possible to write portable code).
And the second with -O3 could not be more wrong, and I can prove it: compile anything with gcc -S -O3 and behold the extra code which a human would never write because it's unnecessary.
- tom_mellior 7y ago> I can prove it: compile anything with gcc -S -O3 and behold the extra code which a human would never write because it's unnecessary. Since you claim that that's true for "anything", care to show us an example consisting of a 5-10 line C function and your assembly version?
- Annatar 7y agoConsidering I wrote above exactly what to do (gcc -S), I believe you are not making this request in good faith; rather, you seek to test whether I know what I'm writing about. However, had you done what I told you to do, you wouldn't have asked. That's why I think you are actually trolling me.
- tom_mellior 7y agoInput code: float dot_product(float *a, float *b, int n) { float result = 0.0f; for (int i = 0; i < n; i++) { result += a[i] * b[i]; } return result; } Output from gcc -O3 -S: .file "foo.c" .text .p2align 4,,15 .globl dot_product .type dot_product, @function dot_product: .LFB0: .cfi_startproc testl %edx, %edx jle .L4 leal -1(%rdx), %eax pxor %xmm0, %xmm0 leaq 4(,%rax,4), %rdx xorl %eax, %eax .p2align 4,,10 .p2align 3 .L3: movss (%rdi,%rax), %xmm1 mulss (%rsi,%rax), %xmm1 addq $4, %rax cmpq %rax, %rdx addss %xmm1, %xmm0 jne .L3 rep ret .p2align 4,,10 .p2align 3 .L4: pxor %xmm0, %xmm0 ret .cfi_endproc .LFE0: .size dot_product, .-dot_product .ident "GCC: 7.4.0" .section .note.GNU-stack,"",@progbits Where's the unnecessary code? What does your hand-optimized version look like? Is your hand-optimized version faster than this?
- Annatar 7y agoThis is how a human would code the same (a human would know, for instance, that (s)he wants to multiply 50 times, passing that in %rax (that would be your "n" in C), something a compiler can never know, which is one of many reasons (and there are many!) why a compiler can never, ever beat a human at coding in assembler: DotProduct: subss %xmm0, %xmm0 .Loop: movss (%rdi, %rax, 4), %xmm1 mulss (%rdi, %rax, 4), %xmm1 addss %xmm1, %xmm0 subq $1, %rax cmpq $0, %rax jnz .Loop ret ...potentially, depending on how 80x86 processors work, the Z flag might be set when subq $1, %rax reaches 0 and the cmpl $0, %rax could be optimized away, making the code even shorter (and faster). But I'll leave it in there because this handily beats the compiler in both size and speed, even with the compiler often trying to cheat with nopw instructions to warm up the data cache. Now compare that with the garbage GCC generated. At -O3 no less! What a bunch of garbage! On a personal note, you forced me to write code for a processor architecture I do not even know (80x86 family of processors in 64-bit mode), at all, so you just assumed my primary system is powered by that intel / AMD garbage of processors, and I don't appreciate that one bit. And you doubted me, which I appreciate even less.
- tom_mellior 7y ago> a human would know, for instance, that (s)he wants to multiply 50 times A human would turn the for loop that counts up into a do-while loop that counts down? That's doable on the C level, and in that case GCC also generates less ceremony: dot_product_2: .LFB1: .cfi_startproc pxor %xmm0, %xmm0 movslq %edx, %rdx .p2align 4,,10 .p2align 3 .L8: movss (%rdi,%rdx,4), %xmm1 mulss (%rsi,%rdx,4), %xmm1 subq $1, %rdx leal 1(%rdx), %eax testl %eax, %eax addss %xmm1, %xmm0 jg .L8 rep ret But of course if you do this you have changed the implied original spec, and you have reassociated a floating-point computation, computing different results. At that point you might as well pass -ffast-math to GCC, and it will beat your code by 8x due to vectorization. > But I'll leave it in there because this handily beats the compiler in both size and speed Not on my machine. On arrays of 1048576 (2 ^ 20) elements I didn't measure any difference between my first variant, the do-while variant and yours. On arrays of 1024 elements I still didn't measure a difference between the two compiled versions, but yours is about 1-2% slower. On 128 elements do-while looks a bit slower than for, and your variant is more than 20% slower. Here are some numbers for n = 128, calling each function 10000000 times. dot_product is the original, dot_product_2 uses do-while, dot_product_3 is your code (with an extra move of edx to rax at the start of the function, of course). dot_product: 1.036514 sec dot_product_2: 1.097186 sec dot_product_3: 1.290411 sec (Edit: This seems to be due to your use of subss instead of pxor to zero xmm0 at the beginning. Removing that gets your code back up to only 1-2% slower than the for loop version.) > even with the compiler often trying to cheat with nopw instructions to warm up the data cache What? > Now compare that with the garbage GCC generated. At -O3 no less! What a bunch of garbage! You still haven't pointed out what exactly you think is garbage. Which line? Which instruction? Or don't you like that it generates some metadata? > you forced me to write code for a processor architecture I do not even know I didn't force you to do anything, I specifically asked you to provide an example of your choice. You could have chosen a piece of code where you knew in advance GCC would do badly. You could have chosen an architecture that you know well and/or one where you know that GCC's backend hasn't had as much work as x86-64. You chose not to do this. I didn't force you to do anything. > And you doubted me Based on the data it looks like I was right to doubt you.