Вот очень простая программа на C:
int main()
{
int n = 0;
while(n != 1000000000){
n += 1;
}
return n;
}
Я скомпилировал ее с помощью Clang и рассчитал время.Он работал за 4.711095243692398e-06
секунды или 0.000004711095243692398
секунды.
Далее я вывожу программу C на язык синтаксической ассемблера Intel, используя проводник компилятора Godbolt (https://godbolt.org) для удаления директив .cfi
:
.file "Svx.c"
.intel_syntax noprefix
.text
.globl main
.type main, @function
main:
push rbp
mov rbp, rsp
mov DWORD PTR -4[rbp], 0
jmp .L2
.L3:
add DWORD PTR -4[rbp], 1
.L2:
cmp DWORD PTR -4[rbp], 1000000000
jne .L3
mov eax, DWORD PTR -4[rbp]
pop rbp
ret
.size main, .-main
.ident "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04.1) 7.4.0"
.section .note.GNU-stack,"",@progbits
Я скомпилировал ее с помощьюGCC и рассчитал время. Результат составил 1.96
секунд - намного медленнее, чем версия Clang.
Наконец, я создал свою собственную версию сборки:
[BITS 64]
[default rel]
global main:function
section .data align=16
section .text
main:
xor rax,rax
l_01:
cmp rax,1000000000
je l_02
add rax,5
jmp l_01
l_02:
ret
Я скомпилировал ее с помощью nasm
и связал его с ld
:
sudo nasm -felf64 Svx.asm
sudo ld -shared Svx.o -o Svx.so
и рассчитал по времени. Он работал за 0.14707629615440965
секунд.
Почему версия C работает так быстро, если обратная компиляцияверсия работает значительно медленнее (0.0000047
секунд против 1.96
секунд), а моя версия NASM работает за 0.147
секунд? У меня такое ощущение, что результат версии C на 0.0000047
секундах неверный, он кажется невероятно быстрым.Это вывод Clang на язык ассемблера:
.text
.intel_syntax noprefix
.file "Svx.c"
.globl main # -- Begin function main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0:
push rbp
.cfi_def_cfa_offset 16
.cfi_offset rbp, -16
mov rbp, rsp
.cfi_def_cfa_register rbp
mov dword ptr [rbp - 4], 0
.LBB0_1: # =>This Inner Loop Header: Depth=1
cmp dword ptr [rbp - 4], 1000000000
je .LBB0_3
# %bb.2: # in Loop: Header=BB0_1 Depth=1
mov eax, dword ptr [rbp - 4]
add eax, 1
mov dword ptr [rbp - 4], eax
jmp .LBB0_1
.LBB0_3:
mov eax, dword ptr [rbp - 4]
pop rbp
.cfi_def_cfa rsp, 8
ret
.Lfunc_end0:
.size main, .Lfunc_end0-main
.cfi_endproc
# -- End function
.ident "clang version 8.0.0-3~ubuntu18.04.1 (tags/RELEASE_800/final)"
.section ".note.GNU-stack","",@progbits
.addrsig
В листинге показано, что они используют стек для переменных, а не регистр, который (обычно) медленнее.
Скорость при0.0000047
секунд, кажется невозможным быстро считатьо миллиард.Если эта скорость верна, в чем ее секрет?Обратный инжиниринг ничего не показывает, и на самом деле версия Godbolt намного медленнее.