int i,j;
for(i=0; i<1000000; i++)
for(j=0;j<1000000; j++)
....
oppure
for(int i=0; i<1000000; i++)
for(int j=0;j<1000000; j++)
....
cioè l'allocazione nello stack di "j" per ogni ciclo di "i" richiede
tempo (suppongo di si perchè viene comunque incrementato e decrementato
ogni volta il registro puntatore allo stack, giusto?)
>secondo voi è più veloce:
Fai un debug del compilato e accertati di cosa fa il tuo compilatore.
Gli ottimizzatori in genere sono abbastanza furbi da usare i registri
del processore come indici, quindi non c'è alcuna allocazione
effettiva su stack.
Non vedo perchè dovrebbe venire incrementato lo stack pointer. A meno
che i .... non contengano chiamate a funzione. Ma in tal caso
l'eventuale incremento dello stack pointer è del tutto trascurabile
rispetto al milione di chiamate a funzione.
E.
> Non vedo perchè dovrebbe venire incrementato lo stack pointer.
Immagino che p51d intenda che ad ogni ciclo venga allocato spazio per
j. Fatto salvo quanto ho detto nella mia risposta (e cioè che molto
presumibilmente il compilatore usa un registro per gli indici dei
cicli), gli ottimizzatori sono comunque abbastanza furbi da spostare -
anche in caso di uso dello stack - l'allocazione al di fuori del
ciclo.
Ho un deja vu, mi pare ne avevamo già discusso. L'allocazione sullo
stack non ha alcun costo, finchè non chiami una funzione.
in
void f() {
int x;
for (int j=0;j<10;j++) {
for (int k=0;j<20;k++) {
// bla bla
}
}
}
quello che presumibilmente farà il compilatore è usare SP per x, SP+1
per j, SP+2 per k. Lo SP non verrà mai modificato.
E.
in gcc 4.2 questo...
int main (int argc, char * const argv[]) {
int k=0;
for(int i=0; i<1000; i++)
for(int j=0; j<1000; j++)
k++;
}
...viene compilato con...
_main:
pushl %ebp
movl %esp, %ebp
subl $24, %esp
movl $0, -20(%ebp)
movl $0, -16(%ebp)
jmp L2
L3:
movl $0, -12(%ebp)
jmp L4
L5:
leal -20(%ebp), %eax
incl (%eax)
leal -12(%ebp), %eax
incl (%eax)
L4:
cmpl $999, -12(%ebp)
jle L5
leal -16(%ebp), %eax
incl (%eax)
L2:
cmpl $999, -16(%ebp)
jle L3
movl $0, %eax
leave
ret
In effetti non mi sembra ci sia nessun incremento/decremento oltre le
variabili del ciclo.
Non saprei, io sono rimasto all'ASM dell'apple ][ (mi pare fosse un
8086). Però DEVE essere così, se venisse cambiato lo SP ogni volta che
viene dichiarata una variabile locale, tutti gli indirizzi relativi alle
altre variabili locali andrebbero modificate.
E.
Approfitto dell'opportunità di vedere il codice macchina, per cercare di
capire che succede. Sarò grato a chi vorrà chiarirmi le idee.
> _main:
> pushl %ebp
salva il valore del registro E sullo stack. Evidentemente la funzione
deve preoccuparsi di ripristinare questo registro che verrà usato in
seguito.
> movl %esp, %ebp
copia lo SP nel registro E
> subl $24, %esp
decrementa lo stack pointer di 24. Probabilmente serve per ignorare gli
argomenti argc e argv e che altro? In che direzione cresce lo stack?
> movl $0, -20(%ebp)
setta a 0 E[-20] (presumibilmente k)
> movl $0, -16(%ebp)
setta a 0 E[-16] (presumibilmente i)
> jmp L2
salta a L2
> L3:
> movl $0, -12(%ebp)
setta a 0 E[-12] (sarebbe j)
> jmp L4
vai a L4
> L5:
> leal -20(%ebp), %eax
> incl (%eax)
viene incrementato k. Strano che non sia in grado di farlo con una unica
istruzione...
> leal -12(%ebp), %eax
> incl (%eax)
viene incrementato j
> L4:
> cmpl $999, -12(%ebp)
> jle L5
controlla che j<=999 e in caso vai a L5
> leal -16(%ebp), %eax
> incl (%eax)
> L2:
> cmpl $999, -16(%ebp)
controlla se i <= 999
> jle L3
e nel caso vai a L3
> movl $0, %eax
Questo forse è il valore di ritorno dal main
> leave
boh?!?! Dovrebbe riprendere il valore di E dallo stack?
> ret
return
E.
> Approfitto dell'opportunità di vedere il codice macchina, per cercare di
> capire che succede. Sarò grato a chi vorrà chiarirmi le idee.
Calcola che il codice e` probabilmente generato con l'ottimizzatore
disabilitato, ho provato a far la stessa cosa con -O3 e mi ha segato del
tutto i loop.
Bye,
Gabry
> Non saprei, io sono rimasto all'ASM dell'apple ][ (mi pare fosse un
> 8086).
Naaaaah... era un 6502 ;-)
Dovrebbe essere del tutto equivalente.
Se l'ottimizzatore decide di posizionare le variabili nello stack,
e questo si sa a compile time, in entrambi i casi lo spazio viene
riservato nello stack frame allocato per la funzione in cui ti trovi,
che ha dimensione nota a compile time e che è allocato nel momento della
chiamata alla funzione, con uno scostamento dallo stack pointer noto a
compile time.
L'ottimizzatore potrebbe sempre decidere di mettere le variabili in un
registro, in quel caso ancora le due versioni sarebbero analoghe.
COnsidera che ad esempio GCC considera lo scope delle variabili, nel
secondo caso, uguale al corpo del for, mentre Visual Studio mantiene i e
j visibili da lì in poi. Ma se non usi i e j più avanti, questo non
dovrebbe far differenza nella scelta tra allocazione nei registri e
nello stack.
ciao
>Ho un deja vu, mi pare ne avevamo già discusso.
Humm... forse non con me...
> L'allocazione sullo
>stack non ha alcun costo, finchè non chiami una funzione.
Sì, hai perfettamente ragione. Ed è esattamente questo che intendo
quando dico che j non richiede alcuna allocazione specifica: viene
gestita al di fuori del ciclo, nella porzione di stack a cui
appartiene.
Esatto, ho disabilitato l'ottimizzazione altrimenti non si capiva niente
('sti compilatori sono troppo intelligenti!).
> Non saprei, io sono rimasto all'ASM dell'apple ][ (mi pare fosse un
> 8086).
:P ...
> Però DEVE essere così, se venisse cambiato lo SP ogni volta che
> viene dichiarata una variabile locale, tutti gli indirizzi relativi alle
> altre variabili locali andrebbero modificate.
Aspetta. Di fatto lo standard non dice *nemmeno* che deve esserci uno
stack (anche se di fatto leggendo la descrizione delle variabili
automatiche sono proprio gestite "come se fossero su uno stack").
Tipicamente molti compilatori semplicemente calcolano tutta la
dimensione necessaria per le variabili locali di una procedura (a
prescindere se ci si arriva veramente in un dato punto) e modificano lo
SP direttamente durante il preludio. Questo per lo meno su architetture
come PPC dove PUSH e POP di fatto non esistono.
Ad ogni buon conto quando usi una PUSH o una POP di fatto *stai*
modificando lo stack pointer. Per cui si modificano tutti gli indirizzi
relativi (allo stack pointer). Non a caso i programmatori ASM usano il
*base* pointer per riferirsi alle variabili locali (%ebp su x86, r30 o
r31 su PPC a seconda dell'ABI, IIRC).
Di fatto in questa maniera hai lo stack pointer che cresce come deve, ma
tu gli indirizzamenti li fai sul base pointer. Poi ci sono opzioni del
compilatore che invece dicono esplicitamente di non usare il base
pointer. Dopo tutto per un umano tenere conto di questi dettagli è
error-prone, per un compilatore no. In questo caso viene fatto tutto con
lo stack pointer. Questo può portare a riduzione della dimensione del
codice e/o della velocità, non so in che misura o in che condizioni (che
potrebbe anche volere dire *mai*, non ho fatto test).
--
-riko
> Approfitto dell'opportunità di vedere il codice macchina, per cercare di
> capire che succede. Sarò grato a chi vorrà chiarirmi le idee.
>
> > _main:
> > pushl %ebp
>
> salva il valore del registro E sullo stack. Evidentemente la funzione
> deve preoccuparsi di ripristinare questo registro che verrà usato in
> seguito.
Altrochè! BTW, ci sono diversi attacchi che sovvertono il comportamento
di un programma basandosi sulla sovrascrittura di %ebp, per quanto meno
eclatanti di quelli che sovrascrivono l'indirizzo di ritorno, presente
poco più in alto sullo stack.
> > movl %esp, %ebp
>
> copia lo SP nel registro E
In particolare nel *base* pointer.
> > subl $24, %esp
>
> decrementa lo stack pointer di 24. Probabilmente serve per ignorare gli
> argomenti argc e argv e che altro? In che direzione cresce lo stack?
No: sta "allocando" spazio per le variabili locali. Lo spazio per argc e
argv lo ha già allocato chi ha chiamato main (spesso una procedura
_start dentro il crt).
Se la cosa è stata fatta su una macchina a 64bit mi torna:
3 interi -> 3 * 8 = 24.
Lo stack cresce 'a seconda dell'architettura'. Su x86 cresce versoil
basso. Ovvero i nuovi stack frame occupano indirizzi numericamente
minori.
> > leal -20(%ebp), %eax
> > incl (%eax)
> viene incrementato k. Strano che non sia in grado di farlo con una unica
> istruzione...
Guarda, a me piace ragionare in termini RISC. A me infastidisce anche
solo che ci sia un'istruzione che modifica direttamente la memoria.
Poi un essere umano potrebbe forse usare registri per i contatori e non
passare mai per lo stack. Anche se farlo su PPC (con una trentina di
registri general purpose liberi) è una cosa diversa che farlo con X86,
> Questo forse è il valore di ritorno dal main
Esatto. Molte ABI sotto x86 fanno ritornare il valore in %eax.
>
> > leave
> boh?!?! Dovrebbe riprendere il valore di E dallo stack?
Fa quello che fa il classico postambolo:
mov %ebp, %esp
pop %ebp
--
-riko
> Naaaaah... era un 6502 ;-)
Io ho qui ancora il manualetto di Monteil... 6502 e 6510!
--
-riko
Non c'è motivo per cui le due forme abbiano un codice compilato diverso.
Il campo di visibilità delle variabili è un concetto che non ha
riscontro nel codice prodotto.
E.
> Non c'e' motivo che le due forme abbiano un codice compilato uguale.
Non sono d'accordo. Il motivo per cui quelle due scritture debbano
avere compilato uguale (ovvero l'implementatore debba sforzarsi di
renderlo analogo a quello che prevede due indici dichiarati a monte
del ciclo, a dispetto della mancanza di vincoli espliciti nello
standard) è insito nella semantica del linguaggio: la dichiarazione
sul posto degli indici è stata introdotta proprio per facilitare e
rendere più sicure le operazioni con i loop. Bisogna incoraggiare i
programmatori ad usarle. Se a questo non segue un'ottimizzazione
spinta del compilato, allora l'implementazione è veramente molto
scarsa e a quel punto meglio cambiare compilatore.
Certo potrebbero essere diversi. Dico che non c'è motivo di farli
diversi se non quello di generare codice più lento, cosa che di solito
si cerca di evitare.
> Il concetto di visibilita' impatta sul codice in quanto viene creato
> spazio per le variabili
> in questione nel blocco di visibilita'.
E perché mai? Il compilatore può riservare lo spazio quando gli sembra
più utile. E nota che "riservare lo spazio" significa solo che il
compilatore decide dove andranno posizionate quelle variabili, non ha
nessun effetto sul codice prodotto.
> All' uscita del blocco, le variabili in questione non sono piu'
> disponibili e sono 'distrutte'.
la chiamata di un eventuale distruttore viene fatta all'uscita dal
blocco. Questo non ha niente a che fare con la memoria occupata dalla
variabile, che può essere riutilizzata subito oppure in seguito.
> Se vuoi dire che l' implementazione dipende dal compilatore che puo'
> generare
> codice uguale, sono d'accordo, ma non c'e' nessuna garanzia che sia cosi'.
> GCC (x86) per esempio calcola lo spazio di stack necessario per tutte le
> variabili
> automatiche una sola volta: in effetti e' come se allocasse tutte le
> variabili
> all' inizio del blocco in un solo colpo, come nei compilatori C di buona
> memoria.
Parlare di "allocazione" in questo caso è parecchio fuorviante. Decidere
dove andranno posizionate le variabili è una operazione che viene fatta
a compile time e quindi non incide sulle prestazioni del codice prodotto.
> Altri compilatori possono non seguire questa regola, specie se si creano
> grosse
> strutture dati automatiche (CodeWarrior for embedded per ppc segue
> questa regola,
Onestamente non capisco come altro si potrebbe fare.
Nel codice
{
int x;
// blocco esterno
{
int y;
// blocco interno
}
}
Quando si entra nel blocco più interno NON HA SENSO modificare lo stack
pointer (SP). Oltre a produrre codice più lento questo renderebbe più
difficile la compilazione. Infatti nel blocco interno la variabile x
avrebbe una posizione diversa rispetto allo SP da quella che avrebbe nel
blocco esterno. Perché mai un compilatore dovrebbe fare questo?
Avere le variabili dichiarate all'interno dei blocchi aiuta il compito
del compilatore, che sa che quella variabile non può essere usata al di
fuori del blocco. Se il compilatore non è in grado di utilizzare questa
informazione per produrre codice più performante, può benissimo
compilare il codice come se tutte le variabili fossero state dichiarate
all'inizio (ovviamente distinguendo eventuali variabili che
incidentalmente hanno lo stesso nome).
E.
> Quando si entra nel blocco più interno NON HA SENSO modificare lo stack
> pointer (SP). Oltre a produrre codice più lento questo renderebbe più
> difficile la compilazione. Infatti nel blocco interno la variabile x
> avrebbe una posizione diversa rispetto allo SP da quella che avrebbe nel
> blocco esterno. Perché mai un compilatore dovrebbe fare questo?
Come ti dicevo, in primo luogo di default i compilatori indirizzano da
un base pointer e non da uno stack pointer (esattamente come gli umani).
Il fatto che su x86 si chiami ebp (e richiami proprio il nome base
pointer) mentre su altre architetture il nome sia meno esplicativo è
completamente secondario.
Poi di fatto puoi anche chiedere al compilatore di indirizzare dallo
stack pointer: l'aritmetica da fare è per un umano tediosa ed
error-prone. Per un compilatore è un giochetto.
A prescindere da questo molte implementazioni *non* modificano lo stack
pointer. Tipo su x86 pure codice come:
#include <stdlib.h>
#define BUF_SIZE (2048-sizeof(int))
struct very_big_area {
int buffer[BUF_SIZE];
size_t length;
};
void f(int x) {
if (x > 0) {
f(x-1);
} else {
struct very_big_area a;
}
}
void g() {
struct very_big_area a;
{
struct very_big_area b;
}
}
genera:
.text
.globl _f
_f:
pushl %ebp
movl %esp, %ebp
subl $8216, %esp
cmpl $0, 8(%ebp)
jle L4
movl 8(%ebp), %eax
decl %eax
movl %eax, (%esp)
call _f
L4:
leave
ret
.globl _g
_g:
pushl %ebp
movl %esp, %ebp
subl $16376, %esp
leave
ret
--
-riko
premettendo che (uso solo x86) condivido l'irrilevanza della
"creazione" di tipo in testa o inline (dato che trattasi
soltanto di aggiungere un offset al registro indice EBP,
quindi che sia una costante o un'altra nulla cambia), mi
sorge però un dubbio ulteriore, e in questo caso mi chiedo
se BRIX possa avere ragione ...
che succede se al posto di un contatore uso un qualche sorta
di iteratore, ossia invece di istanziare un plain int (o
altri tipi intrinseci) istanzio un oggetto dotato di
costruttore esplicito ?
In questo secondo caso, può il compilatore legittimamente
scansarsi la ripetura costruzione (e si presume
inizializzazione) ? Mi parrebbe di no, quindi in questo caso
il costrutto a visibilità ristretta comporterebbe enne
chiamate al costruttore (e magari a NEW), e sarebbe più lento.
Che ne pensate ?
ciao
Soviet:Mario
> Che ne pensate ?
Ribadisco il mio punto di vista: un'implementazione che non riesca ad
ottimizzare l'uso di un indice (fosse anche un iteratore) creando
un'istanza nuova di una classe ad ogni ciclo, quando se ne potrebbe
fare a meno, è un'implementazione scarsa. Ci deve essere un certo
livello di garanzia di ottimizzazione da parte di un'implementazione
perché sia utilizzabile seriamente, anche perché non è possibile
rimettere sempre in gioco aspetti specifici del linguaggio. Il tutto
IMHO, ovviamente.
> In questo secondo caso, può il compilatore legittimamente scansarsi la
> ripetura costruzione (e si presume inizializzazione) ? Mi parrebbe di
> no, quindi in questo caso il costrutto a visibilità ristretta
Quando costruisci un oggetto sullo stack di fatto x il compilatore e`
una displacement new sulla allocazione sullo stack, quindi il
compilatore puo` benissimo riservare lo spazio sullo stack all'entry
point della funzione, ovviamente pero` ad ogni ciclo dovra` chiamare
costruttore e distruttore dell'oggetto... ovviamente non e` detto che
tutti i compilatori si comportino cosi`, ma e` ragionevole che lo
facciano perche` e` il modo + semplice sia come implementazione (non ti
cambiano gli indici ad ogni blocco) che come performance (operazioni
minime sul displacement dello stack pointer).
Non direi, il costruttore deve venire chiamato. Considera questo esempio:
class Int {
int x;
static int count;
Int() {
count++;
x=0;
}
void next() {
++x;
}
bool end() const {
return x==10;
}
static int getCount() {
return count;
}
};
main() {
for (Int i;!i.end();i.next())
for (Int j;!j.end();j.next());
std::cout<< Int::getCount();
}
Non mi dirai che il risultato dipende dall'implementazione...
Lo spazio in cui memorizzare i e j può essere deciso dal compilatore
quando gli pare. Ma il costruttore va chiamato quando glielo dico io!
E.
>
> Non mi dirai che il risultato dipende dall'implementazione...
>
> Lo spazio in cui memorizzare i e j puņ essere deciso dal compilatore
> quando gli pare. Ma il costruttore va chiamato quando glielo dico io!
Ni.
Per questo caso specifico non so risponderti con precisione quindi non
mi pronuncio.
Ma occhio che in generale possono esistere casi in cui un costruttore
non viene chiamato per via di un'ottimizzazione. Per questo motivo da
tutte le parti si raccomanda di stare attenti ai side-effects nei
costruttori!
Il caso che ho in mente (non so se č l'unico ma sicuramente questo
esiste) č la RVO (Return Value Optimization) o la NRVO (Named RVO)
GM
>Non mi dirai che il risultato dipende dall'implementazione...
In questo caso ovviamente no (ti sei volutamente messo nel caso di
codice non ottimizzabile nel senso che dicevo io e grazie tante :-),
ma un ottimizzatore (oggigiorno sono *veramente* furbi) potrebbe
riconoscere il caso in cui chiamare il costruttore ad ogni ciclo può
essere risparmiato. Boh, non so, secondo me è fattibile come controllo
e rientra negli scopi di un ottimizzatore.
Questo proprio non mi e' chiaro per nulla.
Se mai dovrebbe essere implicito proprio l' esatto contrario.
Le definizioni di variabili automatiche dove serve dovrebbe in teoria
garantirmi che se non passo da quel punto del codice, le relative
risorse non dovrebbero nemmeno essere allocate, o almento questa
sembra la modalita' semanticamente piu' ragionevole da assumere
leggendo il codice.
Dal punto di vista semantico non sembrerebbe ragionevole allocare a
priori una quantita' di risorse se non vengono poi utilizzate
perche' a run-time da quel blocco non ci passo.
Questa puo' essere la strada utilizzata dal compilatore per essere
piu' efficiente o solo perche' piu' semplice da implementare, ma
non e' certo implicita nella semantica.
Se scrivo una roba tipo:
.....
switch(p)
{
case X:{
struct {
float p[2000];
}p;
for(int j=0,...)
p[j]= fun(j)...
}break;
case Y:
{
struct {
double p[1000];
}c;
for(int j=0,...)
c[j]= fun(j),
}break;
case W:// Do nothing
break;
default: return;
}
...
L'assunzione che *potrei* fare e mi sembra semanticamente
corretta e' quella di affermare che poiche' i case sono
alternativi, a run-time verra' creata solo la variabile p
o solo la variabile c: non viene creato nulla nel caso W
e si termina la funzione per ogni altro caso.
Quello che farebbe il compilatore probabilmente e' di
allocare comunque su stack uno spazio bastante a contenere
sia p che c, il che non e' efficiente in tutti i casi
(per K o default non serve nemmeno allocare/deallocare spazio).
Oltretutto io sarei anche giustificato a credere che non
venga allocata nessuna variabile in K o default!
> la dichiarazione
> sul posto degli indici è stata introdotta proprio per facilitare e
> rendere più sicure le operazioni con i loop. Bisogna incoraggiare i
> programmatori ad usarle.
Per questo sono piu' che d'accordo...infatti vorrei tanto fosse
una funzionalita' estesa anche ai compilatori puramente C...
> Se a questo non segue un'ottimizzazione
> spinta del compilato, allora l'implementazione è veramente molto
> scarsa e a quel punto meglio cambiare compilatore.
Su questo punto invece mi permetto di dissentire.
_alla_maggior_parte dei programmatori dell' efficienza del
compilatore non importa una pippa (o comunque poco).
Di fatto chi lavora con macchine molto performanti, dotate
di memoria cache e altri arzigoli hardware, per la maggior
parte non si accorge nemmeno della differenza tra un compilatore
molto efficiente e uno meno: in fondo funzionano bene persino
molti liguaggi 'virtualizzati'.
La strategia seguita dai vari compilatori segue la logica
del target di utilizzo. Un compilatore per embedded
presenta problematiche di ottimizzazione diverse da quelle di un
compilatore general-purpose pensato per generare codice su macchine
dove la memoria non e' un problema, tante' che IAR, per esempio,
fa ancora differenza(per efficienza) rispetto ai compilatori tipo
Gcc.
Tra l' altro, se proprio vogliamo buttarla sull' efficienza
le chiamate a funzione espanse nella modalita' classica del C
(con il chiamante responsabile della deallocazione dei parametri
passati) per quanto semplici da implementare(e anche sicure),
sono anche ben poco efficienti sia in termini di velocita' che
di spazio rispetto alla modalita' tipo 'pascal' in cui e' il
chiamato responsabile del consumo delle variabili passate.
Infatti che molti compilatori per embedded (tra cui gli Intel)
prevedono anche la possibilita' di compilare tutte le funzioni
C direttamente come funzioni 'pascal' (o PL/M :-) ).
E le funzioni inline? se ci si prende la briga di verificare l'
espanso assembly a volte si hanno delle belle sorprese...
bye,
brix
Non credo proprio. E' talmente banale e naturale calcolare la posizione
di una variabile sullo stack in funzione della sua profondità
nell'albero dei blocchi di visibilità che non posso immaginare che venga
fatto diversamente.
E.
>Questo proprio non mi e' chiaro per nulla.
Certo, se togli ciò che c'è dopo i due punti, è ovvio che non ti è
chiaro :-) Forse mi sono espresso male (vedi sotto).
>Se mai dovrebbe essere implicito proprio l' esatto contrario.
>Le definizioni di variabili automatiche dove serve dovrebbe in teoria
>garantirmi che se non passo da quel punto del codice, le relative
>risorse non dovrebbero nemmeno essere allocate, o almento questa
>sembra la modalita' semanticamente piu' ragionevole da assumere
>leggendo il codice.
Esatto, questa è una semantica ragionevole. Quello che intendevo dire,
invece, è che se il compilatore mi permette di dichiarare delle
variabili sul posto e le buone regole di programmazione in un
linguaggio mi incoraggiano a fare uso di queste dichiarazioni (perché
gli scope limitati mi proteggono, ecc.., ecc..), l'implementatore
dovrebbe perlomeno sforzarsi di ridurre i costi, perché altrimenti
mando all'aria la buona regola di programmazione e dichiaro le due
variabili fuori dai cicli (ovvio che il vantaggio prestazionale di
un'ottimizzazione di questo tipo è tutto da valutare, ma è un discorso
generale). Cioè, l'implementatore dovrebbere ottimizzare in modo da
rendere *effettivamente* vantaggiose le best practice di
programmazione.
>_alla_maggior_parte dei programmatori dell' efficienza del
>compilatore non importa una pippa (o comunque poco).
E' un altro discorso. Chi sviluppa compilatori ha il dovere di
renderli efficienti. Il fatto che la maggior parte di chi li usa, li
fa girare su piattaforme che rendono impercettibili le differenze di
performance non è una giustificazione per l'inefficienza. Anche perché
questa idea sarebbe assolutamente contraria a quella di progresso
scientifico. E poi io dubito sempre di chi dice "per la maggior parte
della gente vale ..." perché presuppongo che conosca la maggior parte
della gente, il che è assolutamente inverosimile :-)
B.
Non capisco come aggiungere due istruzioni in più possa essere una
ottimizzazione...
E.
Ciao,
Brix
?manu* ha scritto:
> brix99luftballons wrote:
>> č *talmente* banale fare una bel add ebp+size(var) e relatva sub nel
>> blocco
>> che non posso immaginare che venga fatto diversamente perche' in
>> questo caso specifico
>> sarebbe proprio l' ottimizzazione migliore.
>
> Non capisco come aggiungere due istruzioni in piů possa essere una
> ottimizzazione...
Lo e' se lo fai solo dove serve.
> > Certo, se togli ciò che c'è dopo i due punti, è ovvio che non ti è
> > chiaro :-) Forse mi sono espresso male (vedi sotto).
>
> Certo, se non lo avessi riportato due righe sotto :-)
I due punti in grammatica servono appunto a questo.
> Ovvio, ma secondario rispetto all' enunciato ;-)
No, il mio punto era esattamente quello.
> Qui pero' non si parla di *conoscere* le persone (infatti ho scritto:
> "programmatori",
> sottocategoria con peculiarita' specifiche, ridotta parte del genere
> umano, il che se
> non altro, ne riduce di parecchio il numero), ma la tendenza all' uso
> dei linguaggi
> che si intuisce dalle statistiche e dalle tendenze attuali, messe in
> pratica dalla succitata
> categoria.
I programmatori sono persone e "conoscere" può avere significati con
sfumature diverse. Questa parte ridotta del genere umano è in numero
comunque talmente grande che non puoi sapere cosa serva o no a
ciascuno. Chiaro che le valutazioni si possono fare sulle tendenze, ma
quello che posso dire - in base a ciò che vedo - è che se la tendenza
di chi sviluppa compilatori è quella di ottimizzarli e per due motivi:
1) l'uomo tende a fare le cose al meglio, da sempre, secondo un
concetto di "progresso" che è tensione verso l'alto; 2) quelle
ottimizzazioni sono richieste da chi usa quegli strumenti. Non c'è
nessuna statistica (se ce l'hai, mostramela) che dica "il 70% dei
programmatori rinuncerebbe volentieri alle ottimizzazioni". Non credo
ci sia alcuna "tendenza attuale" in questo senso.
> L' ottimizzazione non e' sostanzialmente un requisito che emerge come
> essenziale
Questo semplicemente non è vero. Vedi sopra: se mi dai dei numeri che
dimostrano quanto dici, è un altro discorso.
Aggiungerei che ottimizzare un compilatore ha ricadute su tutto il
software prodotto con quel compilatore. Dunque non è molto diverso
dall'aumentare la potenza della CPU, e probabilmente ha costi molto
minori. Inoltre alcune delle migliorie che vengono fatte alle CPU (ad
esempio l'aggiunta di nuove istruzioni) non avrebbero ricadute sulla
velocità di esecuzione se i compilatori non le sfruttassero.
E.
Vorrei dire che c'è chi usa Java ;-) ma non so se il mio umorismo
sarebbe apprezzato.
In realtà, parlando seriamente, sempre più spesso io vedo la "facilità"
barattata sull'altare delle prestazioni. Dove "facilità" è facilità di
sviluppo, oppure sviluppo con conoscenze algoritmiche ridotte, o
facilità di manutenzione del codice, e così via.
Poi nella realtà non è detto che un programma scritto in C# sia più
"lento" di uno scritto in C++. Dipende dal problema e da chi scrive.
Alla macchina del caffè, con chi sviluppa comunemente in C++ possiamo
parlare di come guadagnare un microsecondo ogni ora, ma non mi è mai
capitato di fare discorsi simili, ribadisco alla macchina del caffè
della mia ditta, con chi sviluppa abitualmente in C#.
>>L' ottimizzazione non e' sostanzialmente un requisito che emerge
>>come essenziale
>
>Questo semplicemente non è vero. Vedi sopra: se mi dai dei numeri che
>dimostrano quanto dici, è un altro discorso.
Se non hai i numeri non puoi dire nemmeno che non è vero, puoi solo
dire che non ci sono prove nè in un senso, nè nel altro. Il mio test
della macchina del caffè acquista così una sua validità :oÞ
--
Articolo 33 della Costituzione italiana: L'arte e la scienza sono
libere e libero ne è l'insegnamento.
Se siete tosti: http://www.rexresearch.com/kervran/kervran.htm
> Vorrei dire che c'è chi usa Java ;-) ma non so se il mio umorismo
> sarebbe apprezzato.
Sto usando Java anch'io negli ultimi anni. Saranno davvero gli
"ultimi" della mia vita se continuerò ad usarlo.
> In realtà, parlando seriamente, sempre più spesso io vedo la "facilità"
> barattata sull'altare delle prestazioni. Dove "facilità" è facilità di
> sviluppo, oppure sviluppo con conoscenze algoritmiche ridotte, o
> facilità di manutenzione del codice, e così via.
Il problema è che si affrontano questi discorsi da due punti di vista
differenti: chi scrive un compilatore non può sottrarsi al dovere di
renderlo efficiente. Mi sembra talmente ovvio da risultare banale.
Invece, chi utilizza un compilatore può rinunciare a fare le cose in
modo efficiente. Sono due concetti molto diversi: tu mi dai uno
strumento potente, efficiente, veloce e io posso scegliere di non
sfruttare appieno la sua potenza, efficienza, velocità; ma quelle
qualità ci sono, è bene che ci siano e che la possa sfruttare quando
mi servono. Se mi privi a priori di quelle qualità, produci un difetto
troppo grave alla base. Insomma, voglio dire che fare questi discorsi
può diventare pericoloso, perché si finisce per avallare cattive
tecniche di programmazione.
> >Questo semplicemente non è vero. Vedi sopra: se mi dai dei numeri che
> >dimostrano quanto dici, è un altro discorso.
>
> Se non hai i numeri non puoi dire nemmeno che non è vero
In questo caso i numeri ce li ho e sono i compilatori presenti in
giro. Trovami un compilatore che non viene appositamente ottimizzato,
per la serie "tanto chi se ne frega delle performance". Neanche i
Javisti arrivano a tanto (e questo è già molto). Posso dire che non è
vero quello che brix dice perché tutti i compilatori presenti sul
mercato sono iper-ottimizzati e perché è la tendenza delle invenzioni
scientifiche (tutte, in qualsiasi campo) quella di progredire
perfezionando il modo di fare certe cose.
Io invece riesco ad evitarlo alla grande :-P
> Il problema è che si affrontano questi discorsi da due punti di vista
> differenti: chi scrive un compilatore non può sottrarsi al dovere di
> renderlo efficiente. Mi sembra talmente ovvio da risultare banale.
Guarda su questo siamo assolutamente d'accordo, però ... però ... si fa
quel che il mercato vuole. Tempo fa, ma sospetto anche oggi, il miglior
generatore di codice ottimizzato era il compilatore Intel. Guardo qui:
http://www.intel.com/cd/software/products/asmo-na/eng/compilers/278609.htm
e vedo che costa sui 600 $, nemmeno una cifra impossibile.
Non ho mai saputo di gente che facesse la fila per comprarlo. Mi viene
da pensare che l'ottimizzazione non è una richiesta top.
E personalmente, nella mia ditta, io lo farei comprare appunto per
questo motivo. Detto tra noi.
> Dunque non è molto diverso
> dall'aumentare la potenza della CPU, e probabilmente ha costi molto
> minori.
Dipende da che punto di vista. Sui costi 'minori' non ci scommetterei
affatto. IMHO al mondo ci sono molto più progettisti di CPU che
matematici/informatici teorici che si dedicano a creare ottimizzazioni
più efficienti.
In assoluto direi che migliorare l'efficienza di un compilatore (nel
senso della capacità di ottimizzare rispetto il codice 'banale') non è
affatto una cosa banale. Anche qui tipicamente il costo viene da
problemi di *correttezza* (e dimostrare la correttezza di un
ottimizzatore è una cosa talmente costosa che spesso non si fa -- con
risultati divertenti sotto gli occhi di tutti --).
Allo stesso modo migliorare una CPU ha dei costi parecchio elevati.
Tuttavia Intel sa *per certo* (o AMD) che venderà almeno x CPU in un
dato anno (poichè ogni computer ha almeno una CPU) con un certo numero
di fluttuazioni. Inoltre se non ottimizza (per quanto marginalmente),
rischia di vendere meno (per la concorrenza).
La concorrenza sui compilatori 'desktop' è quasi trascurabile, invece.
C'è Microsoft (solo su Windows), che fa buoni compilatori. Ma comunque
la gente se sceglie il compilatore Microsoft lo fa anche e soprattutto
per gli ottimi IDE.
C'è Intel. Intel fa compilatori con eccellenti ottimizzatori (e grazie
al cazzo :P). Ma ha una frazione piccola del mercato dei compilatori: di
fatto solo chi fa roba che deve essere veramente molto veloce sta con
Intel.
Comeau è concentrata soprattutto sul versante degli standard. Poi c'è
OpenWatcom, che sigh, non so manco più come sta. Per Borland vale un
discorso simile a quello che si fa con Microsoft.
Oltretutto dal puro punto di vista del compilatore, sia Microsoft che mi
pare Borlando offrono versioni gratuite.
Poi c'è il GCC.
Sull'embedded non entro assolutamente.
Insomma, morale della favola: i soldi con i compilatori li fanno fin li
(mentre ne fanno qualcuno di più con gli ambienti di sviluppo e il
supporto ai programmatori). Con le CPU invece si riescono a fare
parecchi soldi.
> Inoltre alcune delle migliorie che vengono fatte alle CPU (ad
> esempio l'aggiunta di nuove istruzioni) non avrebbero ricadute sulla
> velocità di esecuzione se i compilatori non le sfruttassero.
Dubito che l'aggiunta di istruzioni possa essere in se considerata una
'miglioria'. Ci sono diverse considerazioni (se ti interessa i due libri
di Patterson e Henessey sono piuttosto interessanti a riguardo) che
mostrano come in generale aggiungere un'istruzione è tutt'altro che una
miglioria.
Diverso è aggiungere *set* di istruzioni (SSEx, per dire), ma quelle
sono una miglioria perchè spesso aggiungono o modificano un aggeggio
hardware che va più forte (e le istruzioni sono li per sfruttarlo).
Tipicamente poi le CPU moderne internamente sono macchine RISC con un
numero piuttosto ridotto di operazioni e 'interpretano' le istruzioni
della ISA (specie nel caso di x86).
--
-riko
> Non ho mai saputo di gente che facesse la fila per comprarlo. Mi viene
> da pensare che l'ottimizzazione non č una richiesta top.
Ma non č che gli altri *non* ottimizzano. Gli altri ottimizzano 'meno',
ma se ottimizzano 'abbastanza', quale č il problema?
Oltretutto la differenza fra un programma non ottimizzato e uno
ottimizzato 'poco' (nemmeno 'decentemente') č piuttosto abissale.
Certo, poi se il programma non calcola mai e fa solo gran query al db e
poi le presenta all'utente, probabilmente la differenza quasi non la
vedi. Esattamente come non la vedi se l'applicazione la fai in Python
oppure in qualcosa d'altro.
--
-riko
>> L' ottimizzazione non e' sostanzialmente un requisito che emerge come
>> essenziale
>>
>
> Questo semplicemente non è vero. Vedi sopra: se mi dai dei numeri che
> dimostrano quanto dici, è un altro discorso.
>
Prima una semplice considerazione: ci sono moltissimi compilatori C/C++
di ogni razza
e generere, se si cerca se se trovano a bizzeffe e c'e' chi addirittura
personalizza i compilers
a richiesta.
Ci sono in giro anche moltissimi siti che offrono comparazioni e tra i
tantissimi compilatori
ci sono differenze abissali, di size e velocita' ( 'relativamente'
parlando).
Solo per rimanere in ambito x86, l' intel e' *ottimo* ( ma non del tutto
su architetture AMD ;-) )
eppure non e' tra i piu' gettonati neanche per sviluppo su Intel, mi
pare costi 600$.
In ambiente ARM gcc spopola, ma KEIL e IAR (testati personalmente) sono
decisamente
piu' performanti (per le mie applicazioni). Come mai i suddetti
compilatori non sono i piu'
usati? Eppure il loro ambiente di sviluppo non e' affatto male....
Perche' semplicemente *costano* molto di piu'. Quindi il requisito
fondamentale sembra
essere l'economia dell' ambiente di sviluppo piu' che le performance del
compilatore.
IMHO, ritengo che in generale i compilatori C/C++ recenti siano
abbastanza ottimizzati
per soddisfare la maggior parte degli sviluppatori, che pertanto non lo
ritengono questo
un requisito fondamentale.
Potrei sbagliarmi, ma dammi tu dei dati che *dimostrano* che l'
ottimizzazione
e' cosi' esplicitamente usata come primario requisito per la scelta di
un compiler ;-)
Secondo me non lo e' e ritengo che la maggioranza dei *programmatori*
non se lo ponga
nemmeno come problema.
Seconda considerazione:
http://www.evansdata.com
Fanno ricerche di marketing su prodotti IT edi vario genere, non sono certo
l' unico a conoscere tale organizzazione o equivalente.
Eseguono ciclicamente dei polling su argomenti vari per grosse aziende IT.
Tempo addietro ricevetti un report su IDE e ambienti di sviluppo e
relative statistiche,
Domande tese a valutare editor, debugger, processo di generazione
documetazione/codice
Performance compilatore etc..... ne emergeva che le performance del
compilatore *non*
era al primo posto tra i requisiti.
Le ricerche sono annuali, puoi guardare da te se nell' ultimo report e'
cambiato qualcosa....
Ciao
B.
> Ma non è che gli altri *non* ottimizzano. Gli altri ottimizzano 'meno',
> ma se ottimizzano 'abbastanza', quale è il problema?
Filosofico.
Un conto è dire che esiste il _progresso_ e che quindi i compilatori
ottimizeranno sempre meglio. Usando le parole di AL:
"perché è la tendenza delle invenzioni scientifiche (tutte, in
qualsiasi campo) quella di progredire".
Un conto è essere realistici, e dire che oggi come oggi
l'ottimizzazione non è al primo posto delle richieste del mercato, e
quindi non è nella top list delle cose da fare per chi produce
compilatori.
Ottimizza "abbastanza", va bene.
Poi ci sono le guerre di religione. Sono abbastanza vecchio, forse
decrepito, per ricordare le demenziali discussioni sul VB compilato in
linguaggio macchina e le classi virtuali lente perché c'è un
indirizzamento in più. Un tempo ero più ignorante ed ingenuo e questi
discorsi li facevo pure io.
Detto fuori dai denti non ho mai visto in vita mia qualcuno che usa le
opzioni -o oppure -oo in python.
> Oltretutto la differenza fra un programma non ottimizzato e uno
> ottimizzato 'poco' (nemmeno 'decentemente') è piuttosto abissale.
Rileggendola con attenzione devo dire che hai ragione. Non conta che il
*compilatore* ottimizzi bene, perché spesso bisogna e basta che ci sia
il programmatore che accenda il cervello e scrivi il codice bene, in
modo "ottimizzato".
Guarda è una settimana che sfruttando il tipico calo di lavoro estivo
sto facendo dei test sul come salvare/leggere dei dati binari da
"qualche parte" nel modo più veloce possibile. Ho ancora in sospeso la
storia dei log, ma va be'. Un'idea, scrivo l'implementazione in python,
la test, medito sui risultati. Questo non toglie che un PM demente
(tanto nessun PM della ditta legge questo NG) tempo fa ha avuto un'idea
geniale: usiamo i processori Intel Merced e il C++ andremo come una
scheggia. Il mito della macchina e del progresso su tutto, che devo
dire. Da quel che testo i miei programmelli misti Python/C++ su Intel
con dischi non allo stato dell'arte tengono botta rispetto l'altro team
di sviluppo.
Perché questo esempio? Per dire una cosa molto banale. Prima si
ottimizza il programma, poi si fanno dei test per cercare i colli di
bottiglia, si torna al punto 1, e solo alla fine si passa ad usare un
buon ottimizzatore. Avere un PM che ti fa fretta e ti impedisce di fare
dei test decenti è una penalità che nessun ottimizzatore potrà
recuperare.
Tra l'altro bisogna pure saperli usare gli ottimizzatori. In quanti,
non conosco il termine tecnico, usano le compilazioni per raccogliere
dati statistici e darli al compilatore per permettergli una
ottimizzazione più spinta? Tra l'altro, nella mia abissale ignoranza,
non sapevo che anche il VC 2005 possiede questa caratterisitica.
> Certo, poi se il programma non calcola mai e fa solo gran query al db e
> poi le presenta all'utente, probabilmente la differenza quasi non la
> vedi. Esattamente come non la vedi se l'applicazione la fai in Python
> oppure in qualcosa d'altro.
Guarda, tra un buon progettista e programmatore in Python può
massacrare a livello di prestazioni un mediocre progettista e
programmatore in C++. Di solito un programma ad uso intensivo di CPU da
qualche parte deve leggere dei dati, li deve elaborare, e poi deve
mettere i risultati da qualche parte. C'è moltissimo spazio per
scrivere con saggezza un programma del genere.
Poi alla fine, per me che lavoro su x86, propongo al capo di prendere
il compilatore Intel che costa molto meno di una settimana di mio
lavoro di fine-tuning e sicuramente da risultati migliori.
Proposta ad oggi sempre bocciata, tanto il fine-tuning non serve :-)
... e il debug lo farà il cliente :-)