Newsgroup: it.comp.lang.c++
Titolo: ottimizzazioni non gradite

Salve a tutto il NG,
e' la prima volta che scrivo ma non sono a crudo di C++.
Vorrei portare alla vostra attenzione un'ottimizzazione che il 
compilatore g++ continua costantemente ad operare durante la fase di 
compilazione di un programma su cui sto lavorando.
Veniamo al dunque, prendete questo piccolo pezzo di codice:
countError = 0;
size = p->getSize();
backbone = new rightType[size];
p->rewind();
for(int i = 0; i < size; i++) {
     backbone[i] = p->getNextSomething();
     if (backbone[i].error) contError++;
}
Nel codice p punta all'istanza di una classe (oggetto) che gestisce una 
sorta di lista (non vado nel dettaglio e non ditemi di utilizzare le STL 
tanto p fa cose ben diverse), la funzione membro getNextSomething() può 
essere invocata quante volte si vuole, al massimo in error verrà 
inserito un codice d'errore tipo NO_NEXT_SOMETHING. La classe p è 
testata in modo esaustivo ed è perfettamente funzionante.
Secondo voi se size vale per esempio 100, quanti cicli compie il for?
La sconcertante risposta è: 1 (sia che size sia 1,2,3,...,100000,...).
Perché accuso g++ di ottimizzare il codice? Semplice, trasformate il 
precedente codice in:
countError = 0;
size = p->getSize();
backbone = new rightType[size];
p->rewind();
for(int i = 0; i < size; i++) {
     backbone[i] = p->getNextSomething();
     if (backbone[i].error) {
         contError++;
         cout << "At the moment error counting reach: " \
              << countError << endl;
     }
}
e magicamente vengono compiute esattamene size cicli (sia che size sia 
100, sia che size sia 1000 etc.)
Qualcuno sa come risolvere il problema?
Io, al momento, non ho trovato soluzione.
Ringrazio tutti di cuore per l'attenzione
Carmine.

Nel precedente messaggio ho commesso un errore di battitura,
dove leggete contError sostituite con countError
Scusate l'errore.
Carmine

 La sconcertante risposta č: 1 (sia che size sia 1,2,3,...,100000,...).
Se non ci sono effetti collaterali il compilatore č autorizzato ad eliminare 
l'intero ciclo. Ecco perché quando invece aggiungi una stampa a video il 
compilatore non puň piů applicare l'ottimizzazione.
EMG
--
EMAG Software Homepage:     
http://www.emagsoftware.it
The EMG's ZX-Spectrum Page: 
http://www.emagsoftware.it/spectrum
The Best of Spectrum Games: 
http://www.emagsoftware.it/tbosg
The EMG Music page:         
http://www.emagsoftware.it/emgmusic
 

 
Questo č vero, ma la chiamata a getNextSomething() restituisce di volta 
in volta elementi diversi che vanno a riempire un vettore, come fare per 
dire al compilatore di non ottimizzare?
Carmine

g++ -O0 ?


 g++ -O0 ?
Già provato, non funziona :-(

 al compilatore di non ottimizzare?
Credo basti che poi con quei valori tu ci faccia qualcosa che altera in 
qualche modo l'ambiente. Se restano lettera morta e scompaiono con il 
programma allora il compilatore, pur eseguendo quella ottimizzazione, non 
influisce sul risultato.


 influisce sul risultato.
Hai messo in luce un fatto che avevo dimenticato di menzionare.
Riscrivo qui il pezzo di codice cambiando semplicemente il nome di una 
variabile per maggiore leggibilitŕ del codice:
welldone = 0;
size = p->getSize();
backbone = new rightType[size];
p->rewind();
for (int i = 0; i < size; i++) {
     backbone[i] = p->getNextSomething();
     if(backbone[i].error == NO_ERROR) welldone++;
}
somethingelse = new someType[welldone];
questo dovrebbe essere una situazione in cui il compilatore non effettua 
nessuna ottimizzazione poiché il valore di welldone puo' variare 
all'interno del ciclo.
Purtroppo, per quanto riguarda il g++, non č cosě.
Tra l'altro questa č proprio la situazione che mi si presenta nel mio 
codice.
Purtroppo non ho modo di provarlo con altri compilatori, quindi al 
momento lo proporrei come un bug di g++, a meno dell'esistenza di 
qualche flag.
Vorrei poi farvi notare una variante del codice di cui sopra:
int i;
welldone = 0;
size = p->getSize();
backbone = new rightType[size];
p->rewind();
for (i = 0; i < size; i++) {
     backbone[i] = p->getNextSomething();
     if(backbone[i].error == NO_ERROR) welldone++;
}
somethingelse = new someType[welldone];
in questo caso i č definita come variabile globale (o comunque visibile 
all'esterno del ciclo for), se si prova a vedere il valore di i alla 
fine del ciclo risulterŕ naturalmente uguale a quello di size (nel caso 
ovviamente di size >= 0, sarŕ 0 nel caso di size < 0).
Il compilatore quindi sa che i alla fine del ciclo deve avere un certo 
valore. Ma anche in questo caso p->getNextSomething() č chiamata una 
sola volta.
Grazie Enrico Maria per aver sottolineato la mia mancaza e per avermi 
permesso di aggiungere altro "pepe".
Carmine

 La sconcertante risposta č: 1 (sia che size sia 1,2,3,...,100000,...).
Se accade veramente questo, si tratta di un baco del compilatore. Potresti 
indicare che versione del compilatore stai usando e fornire anche una 
variante compilabile del codice che manifesti lo stesso problema?
Max


La versione č:
gcc 3.4.4 (cygwin special) (gdc 0.12, using dmd 0.125)
ma ho provato anche su piattaforma Linux con:
gcc version 3.3.5 (Debian 1:3.3.5-8ubuntu2)
stesso problema ovviamente.
Una variante del codice?
A quest'ora di notte?
Adesso vado a nanna, al mio risveglio ne scrivo un paio, le testo e se 
succede la stessa cosa le posto. Scusa se non lo faccio subito ma sono
stanco morto.
Carmine

 Purtroppo, per quanto riguarda il g++, non è così.
Ma poi con quel valore ci fai qualcosa che altera in qualunque modo l'ambiente 
(stampa a video, scrittura su file, etc.)? Altrimenti non ci sarà comunque 
differenza di risultato tra ottimizzazione e non (anche se, in effetti, non so 
quanto un compilatore possa essere intelligente al riguardo).


 size = p->getSize();
penso che il tuo compilatore sia vecchio; con codice del tutto simile
al tuo non ho mai avuto problemi.  prova a dichiarare 'p' volatile;
probabilmente in questo modo tutti i metodi che fanno riferimento a
'p' non vengono ottimizzati.  ma il compilatore puo' essere vecchio e
bacato anche a questo riguardo...
-- 
Maurizio Loreti                         
http://www.pd.infn.it/~loreti/mlo.html
Dept. of Physics, Univ. of Padova, Italy              ROT13: 
ybe...@cq.vasa.vg

 Se accade veramente questo, si tratta di un baco del compilatore.
In effetti non avevo considerato che se lui riesce a vedere quei valori 
significa che li ha stampati e quindi il compilatore non avrebbe dovuto 
ottimizzare. Se le cose stanno cosě allora sě, sembra proprio un bug.


 La sconcertante risposta č: 1 (sia che size sia 1,2,3,...,100000,...).
Uhm? La tua ipotesi mi suona mooolto strana...
...ma non si sa mai! :-)
Nel primo caso come fai a capire che il ciclo viene eseguito una sola volta?
Hai provato a var genereare il codice assembly al g++ (opzione -S) e darci
un'occhiata?
In ogni caso, quale versione del g++ stai usando?
-- 
Qual'e' il colmo per uno studente-lavoratore?
Avere un quadrato che si crede un rettangolo.

 Carmine
L'altro mio post si è perso tra le nuvole :-(
credo che il problema sia nella riga:
  ... = p->getNextSomething();
il compilatore forse pensa sia invariante e probabilmente lo ha portato 
fuori dal ciclo.
Prova a passare 'i' come argomento fittizio al metodo 
getNextSomething(). Naturalmente il compilatore non deve vederne 
l'implementazione.

 
LA stessa cosa che ho pensato pure io... basterebbe prendere il codice 
che non funziona cosi' come e' e  prima della fine del programa fare ad 
esempio un salvataggio su file di alcuni elementi di backbone... e 
naturalemnte verificare che il metodo getNextSOmething effettivamente 
restituisca qualcosa di sensato e che dipenda da qualcosa di computato o 
esterno (randomm?? )
Fatto questo, eseguire e verificare se l'ottimizazione sparisce.
IMHO

Scusa ma xchè vi ostinate a usare g++ ? io uso devc++ 4.9.9.1 va na
meraviglia :-D saluti

 meraviglia :-D saluti
erh
lo sai, vero, anche tu stai usando il g++?
-- 
Firma in costruzione

 meraviglia :-D saluti
devc++ : e' un IDE
g++    : e' un compilatore
e secondo te devc++ che compilatore usa ?
;-)
-- 
Ciao!

 ha scritto nel messaggio
 > news:%gtaf.16827$hC1....@tornado.fastwebnet.it...
 >>Secondo voi se size vale per esempio 100, quanti cicli compie
 >>il for?  La sconcertante risposta è: 1 (sia che size sia
 >>1,2,3,...,100000,...).
 > Se non ci sono effetti collaterali il compilatore è
 > autorizzato ad eliminare l'intero ciclo. Ecco perché quando
 > invece aggiungi una stampa a video il compilatore non può più
 > applicare l'ottimizzazione.
Ma solo se non ci sono effetti collaterali. Se
p->getNextSomething() non è visibile, il compilatore non sa se
ci sono effetti collaterali o non.
Senza vedere più di codici, mi è impossibile a dire da dove
viene l'errore, ma ho già fatto cicli come:
     for ( int i = 0 ; i < 100 ; ++ i ) {
         someFunction() ;
     }
senza problemi mit g++. E someFunction() viene chiamata 100
volte.
-- 
James Kanze                                 mailto: 
james...@free.fr
Conseils en informatique orientée objet/
                        Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France 
+33 (0)1 30 23 00 34

 
ehm come gia' detto devc++ e' una "maschera" per g++ o meglio in IDE .. 
l'unica differenza e' che forse il compilatore mingw puo' avere 
qualcosina di idverso ma dubito..un consiglio usa mingw developer studio

logicMind wrote:
 > Carmine Cairo ha scritto:
 >> Enrico Maria Giordano wrote:
     [...]
 > credo che il problema sia nella riga:
 >  ... = p->getNextSomething();
 > il compilatore forse pensa sia invariante e probabilmente lo
 > ha portato fuori dal ciclo.
G++ pensa una funzione è pura in due case: vede il sorgente,
l'analisa, e prova ch'è pura, o la funzione era dichiarata con
l'attributa "pure", cioà:
     SomeType getNextSomething() __attribute__((pure)) ;
(È un'estensione g++, non C++ standard.)
È sempre possibile che c'è un errore nel compilatore, ma ho
bisogna di vedere più di codice primo di dirlo.


 
Mi è capitato col VisualC++ che il seguente codice non terminasse mai:
SetEvent(hEvClose);
while(running)
   Sleep(100);
per qualche sua ragione aveva messo 'running' in EAX e faceva sempre il 
test sul registro (se non ricordo male avevo anche provato a dichiararla 
'volatile').
Modificata in:
SetEvent(hEvClose);
while(true) {
   if(!running)
     break;
   Sleep(100);
   }
ha preso a funzionare.



 >>  >> Enrico Maria Giordano wrote:
 >>     [...]



 >>     SomeType getNextSomething() __attribute__((pure)) ;



 > SetEvent(hEvClose);
 > while(running)
 >   Sleep(100);

 > Modificata in:

 > ha preso a funzionare.
Per caso.
Cosa dice la documentazione del sistemo? Secondo Posix, i due
cicli non sono legale che se nessu non cambia running. Se no,
bisogna di un sincronisazione : un lock o qualcosa. Ma certi
sistemi Posix danno più di garanzie.
Conosco abbastanze poco Windows, ma mi sembra che qui, si vuole
anche richieste sisteme, qualcosa come:
     while ( InterlockedExchange( &running, true ) ) {
         //  ...
     }
In ogni caso, le garanzie vengono dal sisteme, non dal C++.


> La sconcertante risposta è: 1 (sia che size sia 1,2,3,...,100000,...).
