J'ai écrit ces deux solutions pour Projet Euler Q14 en assembleur et en C++. Il s'agit de la même approche de force brute identique pour tester les conjecture de Collatz . La solution de montage a été assemblée avec
nasm -felf64 p14.asm && gcc p14.o -o p14
Le C++ a été compilé avec
g++ p14.cpp -o p14
Montage, p14.asm
section .data
fmt db "%d", 10, 0
global main
extern printf
section .text
main:
mov rcx, 1000000
xor rdi, rdi ; max i
xor rsi, rsi ; i
l1:
dec rcx
xor r10, r10 ; count
mov rax, rcx
l2:
test rax, 1
jpe even
mov rbx, 3
mul rbx
inc rax
jmp c1
even:
mov rbx, 2
xor rdx, rdx
div rbx
c1:
inc r10
cmp rax, 1
jne l2
cmp rdi, r10
cmovl rdi, r10
cmovl rsi, rcx
cmp rcx, 2
jne l1
mov rdi, fmt
xor rax, rax
call printf
ret
C++, p14.cpp
#include <iostream>
using namespace std;
int sequence(long n) {
int count = 1;
while (n != 1) {
if (n % 2 == 0)
n /= 2;
else
n = n*3 + 1;
++count;
}
return count;
}
int main() {
int max = 0, maxi;
for (int i = 999999; i > 0; --i) {
int s = sequence(i);
if (s > max) {
max = s;
maxi = i;
}
}
cout << maxi << endl;
}
Je connais les optimisations du compilateur pour améliorer la vitesse et tout le reste, mais je ne vois pas beaucoup de moyens d'optimiser davantage ma solution en assembleur (en parlant de manière programmatique et non mathématique).
Le code C++ a un modulus à chaque terme et une division à chaque terme pair, alors que l'assemblage n'a qu'une division par terme pair.
Mais l'assemblage prend en moyenne 1 seconde de plus que la solution C++. Comment cela se fait-il ? Je vous le demande principalement par curiosité.
Temps d'exécution
Mon système : Linux 64 bits sur Intel Celeron 2955U 1.4 GHz (microarchitecture Haswell).
-
g++
(non optimisé) : 1272 ms en moyenne -
g++ -O3
moyenne 578 ms -
original asm (div) avg 2650 ms
-
Asm (shr)
moyenne 679 ms -
@johnfound asm , assemblé avec nasm avg 501 ms
-
@hidefromkgb asm 200 ms en moyenne
-
@hidefromkgb asm optimisé par @Peter Cordes moyenne de 145 ms
-
@Veedrac C++ 81 ms en moyenne avec
-O3
, 305 ms avec-O0