Sto parlando solo dal lato C (e quindi applicabile a C ++). Non ho un sistema in grado di eseguire C # da cui lavorare.
Il primo programma che ho scritto è stato banale:
#include <math.h>
#include <stdio.h>
int main(void) {
printf("%f\n",sqrt(2.0));
}
Uso di gcc -S -O3 sqrt.c
Ho ottenuto il sorgente compilato in sqrt.s e l'ho esaminato.
.file "sqrt.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC1:
.string "%f\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB14:
.cfi_startproc
movsd .LC0(%rip), %xmm0
movl $.LC1, %edi
movl $1, %eax
jmp printf
.cfi_endproc
.LFE14:
.size main, .-main
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC0:
.long 1719614413
.long 1073127582
.ident "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]"
.section .comment.SUSE.OPTs,"MS",@progbits,1
.string "Ospwg"
.section .note.GNU-stack,"",@progbits
Si noterà che non vi è alcuna chiamata a sqrt nel codice - sembra che stia semplicemente caricando una costante (che è).
Questo è diventato più evidente durante la scrittura di uno che utilizzava una variabile e faceva la compilazione per dimostrare come sarebbe una chiamata a sqrt
.
Non ho alcun tipo di eleganza con questo codice.
#include <math.h>
#include <stdio.h>
void main(int argc, char **argv) {
double num = atoi(argv[0]);
printf("%f\n",sqrt(num));
}
Mentre gcc -O3 -S sqrt.c
ha funzionato, questo secondo programma come gcc -O3 -S sqrt2.c
ha restituito
/tmp/cckmgfMS.o: In function 'main':
sqrt2.c:(.text+0x46): undefined reference to 'sqrt'
collect2: ld returned 1 exit status
chiamava sqrt
e ho dimenticato di collegare la libreria matematica.
Quando aggiungi il link al codice, puoi vedere la chiamata a sqrt al suo interno:
.file "sqrt2.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC0:
.string "%f\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB14:
.cfi_startproc
subq $8, %rsp
.cfi_def_cfa_offset 16
movq (%rsi), %rdi
xorl %eax, %eax
call atoi
cvtsi2sd %eax, %xmm1
sqrtsd %xmm1, %xmm0
ucomisd %xmm0, %xmm0
jp .L5
.L2:
movl $.LC0, %edi
movl $1, %eax
addq $8, %rsp
.cfi_remember_state
.cfi_def_cfa_offset 8
jmp printf
.L5:
.cfi_restore_state
movapd %xmm1, %xmm0
call sqrt
jmp .L2
.cfi_endproc
.LFE14:
.size main, .-main
.ident "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]"
.section .comment.SUSE.OPTs,"MS",@progbits,1
.string "Ospwg"
.section .note.GNU-stack,"",@progbits
Si può vedere in questo codice la chiamata a sqrt e la mancanza delle costanti introdotte dall'ottimizzatore.
Dal commento precedente:
I have of course store the 10^6 sqrt(2.0) calls by doing a sum in a
variable ( i.e: var += sqrt(2.0) ) and print it on screen at the end
to be sure that compilator will not skip some codes. – Guillaume07 Dec
24 '12 at 19:19
Quindi, considera - se hai a che fare con costanti, questo è qualcosa che gli ottimizzatori C e C ++ identificheranno e ottimizzeranno.
Non avendo accesso a C #, ho visto come Java si occupa della linea:
System.out.println(Math.sqrt(2.0));
Questa istruzione è compilata nel codice byte Java di:
0 getstatic java.lang.System.out : java.io.PrintStream [16]
3 ldc2_w <Double 2.0> [22]
6 invokestatic java.lang.Math.sqrt(double) : double [24]
9 invokevirtual java.io.PrintStream.println(double) : void [30]
Si può vedere che il compilatore Java non ha accesso alle informazioni dell'output di sqrt()
per essere in grado di ottimizzare in una costante. È possibile che l'ottimizzatore JIT abbia accesso alle informazioni sulla purezza delle chiamate attraverso Math
a StrictMath
e sostituisca più chiamate di Math.sqrt(2.0)
allo stesso valore (e non lo richiami di nuovo), tuttavia ha ancora chiamarlo una volta a quel punto per ottenere il valore. Detto questo, non ho idea di ciò che accade a runtime nel JIT e di come le chiamate a funzioni pure che finiscono in nativo potrebbero essere ottimizzate.
Tuttavia, l'ottimizzatore C è ancora in testa al gioco con un grosso ciclo (supponendo che l'ottimizzatore JIT debba solo effettuare una chiamata a sqrt () per ottenere quel primo valore).
Osservando l'ottimizzazione del ciclo in C, l'ottimizzatore calcola anche il ciclo.
#include <math.h>
#include <stdio.h>
int main(void) {
double sum = 0;
int i = 0;
for(i; i < 10; i++) {
sum += sqrt(2.0);
}
printf("%f\n",sum);
}
attraverso gcc -O3 -S sqrt3.c
(ancora non necessario -lm) diventa:
.file "sqrt3.c"
.section .rodata.str1.1,"aMS",@progbits,1
.LC1:
.string "%f\n"
.text
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB14:
.cfi_startproc
movsd .LC0(%rip), %xmm0
movl $.LC1, %edi
movl $1, %eax
jmp printf
.cfi_endproc
.LFE14:
.size main, .-main
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC0:
.long 2034370
.long 1076644038
.ident "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]"
.section .comment.SUSE.OPTs,"MS",@progbits,1
.string "Ospwg"
.section .note.GNU-stack,"",@progbits
E si può vedere che questo codice è identico al primo, con costanti diverse nella sezione .LC0
. Il ciclo è stato calcolato solo per "il valore finale è questo, non preoccuparti di farlo in fase di esecuzione".