OK, here it is:
First my well known Nim test code:
# nim c -d:release k.nim
import random, math
proc main =
var s, j: int
for i in 0 .. 10000000:
j = rand(7)
#s += j * j # j ^ 2
s += j ^ 2
echo s
main()
Run
Compiled with nim c -d:release k.nim with gcc9.1
.file "k.c"
.L33:
movl $7, %edi
call rand_v7jZDEs4VOsrcpvk0yo8Rg
movq %rax, %rdi
movl $2, %esi
call roof__e6fgxN584SyDK8XF8s1uig
addq %rax, %rbp
decq %rbx
jne .L33
Run
.file "stdlib_math.c"
.text
.p2align 4
.globl roof__e6fgxN584SyDK8XF8s1uig
.hidden roof__e6fgxN584SyDK8XF8s1uig
.type roof__e6fgxN584SyDK8XF8s1uig, @function
roof__e6fgxN584SyDK8XF8s1uig:
.LFB3:
.cfi_startproc
movq %rdi, %rax
cmpq $2, %rsi
je .L2
jg .L3
testq %rsi, %rsi
je .L9
cmpq $1, %rsi
jne .L5
ret
.p2align 4,,10
.p2align 3
.L3:
cmpq $3, %rsi
jne .L5
movq %rdi, %rdx
imulq %rdi, %rdx
imulq %rdx, %rax
ret
.p2align 4,,10
.p2align 3
.L9:
movl $1, %eax
ret
.p2align 4,,10
.p2align 3
.L5:
movq %rax, %rdx
movl $1, %eax
jmp .L7
.p2align 4,,10
.p2align 3
.L22:
imulq %rdx, %rdx
.L7:
testb $1, %sil
je .L8
imulq %rdx, %rax
.L8:
shrq %rsi
jne .L22
ret
.p2align 4,,10
.p2align 3
.L2:
imulq %rdi, %rax
ret
.cfi_endproc
.LFE3:
.size roof__e6fgxN584SyDK8XF8s1uig,
.-roof__e6fgxN584SyDK8XF8s1uig
.ident "GCC: (Gentoo 9.1.0 p1.0) 9.1.0"
.section .note.GNU-stack,"",@progbits
Run
Seems to be not surprising. As roof proc is not inlined, full assembler proc is
called.
And to verify here timing with your roof proc:
$ time ./k
175032899
real 0m0.172s
Run
Plain j * j gives
$ time ./k
175032899
real 0m0.164s
Run
It is not a big difference of course, but note the rand() and loop overhead.
For Python we would be happy with the roof proc, but this is Nim. And as I
wrote above, my static suggestion gives same as j * j. Of course we can use
-flto, and maybe we should make that the default, then all is inlined and your
roof proc works perfectly.