* * * * *
More stupid benchmarks about compiling a million lines of code
I'm looking at the code GCC (Gnu's Not Unix Compiler Collection) [1] produced
for the 32-bit system (I cut down the number of lines of code [2]):
-----[ data ]-----
804836b: 68 ac 8e 04 08 push 0x8048eac
8048370: e8 2b ff ff ff call 80482a0 <puts@plt>
8048375: 68 ac 8e 04 08 push 0x8048eac
804837a: e8 21 ff ff ff call 80482a0 <puts@plt>
804837f: 68 ac 8e 04 08 push 0x8048eac
8048384: e8 17 ff ff ff call 80482a0 <puts@plt>
8048389: 68 ac 8e 04 08 push 0x8048eac
804838e: e8 0d ff ff ff call 80482a0 <puts@plt>
8048393: 68 ac 8e 04 08 push 0x8048eac
8048398: e8 03 ff ff ff call 80482a0 <puts@plt>
804839d: 68 ac 8e 04 08 push 0x8048eac
80483a2: e8 f9 fe ff ff call 80482a0 <puts@plt>
80483a7: 68 ac 8e 04 08 push 0x8048eac
80483ac: e8 ef fe ff ff call 80482a0 <puts@plt>
80483b1: 68 ac 8e 04 08 push 0x8048eac
80483b6: e8 e5 fe ff ff call 80482a0 <puts@plt>
80483bb: 83 c4 20 add esp,0x20
-----[ END OF LINE ]-----
My initial thought was Why doesn't GCC (Gnu's Not Unix Compiler Collection)
just push the address once? but then I remembered that in C, function
parameters can be modified. But that lead me down a slight rabbit hole in
seeing if printf() (with my particular version of GCC) even changes the
parameters. It turns out that no, they don't change (your mileage may vary
though). So with that in mind, I wrote the following assembly code:
-----[ Assembly ]-----
bits 32
global main
extern printf
section .rodata
msg:
db 'Hello, world!',10,0
section .text
main:
push msg
call printf
;; 1,999,998 more calls to printf
call printf
pop eax
xor eax,eax
ret
-----[ END OF LINE ]-----
Yes, I cheated a bit by not repeatedly pushing and popping the stack. But I
was also interested in seeing how well nasm [3] fares compiling 1.2 million
lines of code. Not too badly, compared to GCC:
-----[ shell ]-----
[spc]lucy:/tmp>time nasm -f elf32 -o pg.o pg.a
real 0m38.018s
user 0m37.821s
sys 0m0.199s
[spc]lucy:/tmp>
-----[ END OF LINE ]-----
I don't even need to generate a 17M (Megabyte) assembly file though, nasm can
do the repetition for me:
-----[ Assembly ]-----
bits 32
global main
extern printf
section .rodata
msg: db 'Hello, world!',10,0
section .text
main: push msg
%rep 1200000
call printf
%endrep
pop eax
xor eax,eax
ret
-----[ END OF LINE ]-----
It can skip reading 16,799,971 bytes and assemble the entire thing in 25
seconds:
-----[ shell ]-----
[spc]lucy:/tmp>time nasm -f elf32 -o pf.o pf.a
real 0m24.830s
user 0m24.677s
sys 0m0.144s
[spc]lucy:/tmp>
-----[ END OF LINE ]-----
Nice. But then I was curious about Lua. So I generated 1.2 million lines of
Lua:
-----[ Lua ]-----
print("Hello, world!")
-- 1,999,998 more calls to print()
print("hello, world!")
-----[ END OF LINE ]-----
And timed out long it took Lua to load (but not run) the 1.2 million lines of
code:
-----[ shell ]-----
[spc]lucy:/tmp>time lua zz.lua
function: 0x9c36838
real 0m1.666s
user 0m1.614s
sys 0m0.053s
[spc]lucy:/tmp>
-----[ END OF LINE ]-----
Sweet!
[1]
https://gcc.gnu.org/
[2]
gopher://gopher.conman.org/0Phlog:2019/10/04.2
[3]
https://nasm.us/
Email author at
[email protected]