Newsgroup: it.comp.programmare.win32
Titolo: [VC] inline

Ciao a tutti,
ho un problema con la direttiva inline per la dichiarazione di una funzione
all'interno di una classe. la classe in questione contiene un membro privato
m_pImage che è un puntatore a DWORD (punta ad un array).
ora, se dichiaro una funzione come inline ed in essa leggo soltanto questo
array, questa mi risulta molto veloce (come è auspicabile).
se al contrario nella stessa funzione sempre dichiarata come inline provo a
scrivere un valore qualsiasi in questo array, la funzione risulta
sensibilmente più lenta (con la prima, ottengo un ritardo di 0 ms su un
miliardo di iterazioni, con la seconda ottengo dei ritardi di 10-20ms già
con un milione di iterazioni. le misurazioni le ho fatte con la
GetTickCount, che ha una precisione di 10ms circa). questo mi fa pensare che
il compilatore non riesca a compilare effettivamente come inline quando
scrivo nell'array, cosa può essere la causa? c'è modo di aggirare l'ostacolo
ottenendo una funzione molto veloce anche per la scrittura?
grazie in anticipo, assa.
--
gosclient: 
http://members.xoom.it/gosclient/
join the mailing list: 
http://groups.yahoo.com/group/gosclient/join
icq: #215876894

 grazie in anticipo, assa.
scrivi come e cosa ???
usi = su oggetti richiedendo un oggetto temporaneo magari e cosi' facendo tempo 
di creazione e distruzioni maggiori ???
chiami funzioni esterne di libreria o di una dll ???
se facevi un piccolo esempio di quello che facevi magari qualcuno ti riusciva a 
spiegare dove stai perdendo tempo nell'algoritmo ...
ciao

Ciao, assa!
Il giorno 20/10/2003 alle ore 20:44 hai scritto:
 a> pensare che il compilatore non riesca a compilare effettivamente come
 a> inline quando scrivo nell'array, cosa può essere la causa? c'è modo di
Non e' che semplicemente in lettura i dati sono nella cache del processore e
vengono letti velocemente, mentre per la scrittura deve accedere alla ram e
quindi e' piu' lento ?
Bye

 [...]
Oltre alla validissima considerazione di "1sconuscioto",
aggiungo che le inline con cicli for() non possono essere espanse inline dal
compilatore MS VC++. Ovviamente questo vale se hai usato un ciclo. Ti
consiglio di postare i due codici come esempio.
Ciao. Federico.

avete tutti ragione, non avendo postato nessun esempio non si capiva
benissimo la mia situazione, ecco a voi una cosa simile a quella che ho
fatto io:
class Immagine {
    private:
        int m_w;        //contiene la larghezza dell'immagine
        int m_h;        //contiene l'altezza dell'immagine
        DWORD* m_pImage;    //contiene un puntatore all'immagine
    public:
        BOOL Create(int nWidth, int nHeight);
        inline void SetPixel(int x, int y, COLORREF cr)
        {
            m_pImage[(y * m_w) + x] = cr;
        };
        inline COLORREF GetPixel(int x, int y)
        {
            return m_pImage[(y * m_w) + x];
        };
};
come potete vedere la situazione è molto semplice. tramite la Create, la
classe alloca un array di DWORD (veramente è il sistema operativo ad
allocarlo per me, tramite la CreateDIBSection), ne mette un puntatore in
m_pImage, l'altezza dell'immagine in m_h e la sua larghezza in m_w. Poi, con
chiamate successive a GetPixel e SetPixel (che come potete vedere sono
estremamente semplici) si possono cambiare i colori dell'immagine. I tempi
di esecuzione sono quelli che vi ho detto prima, quindi 0ms per 1 miliardo
di esecuzioni della GetPixel e fra 10 e 20 ms di ritardo per un solo milione
di iterazioni per quanto riguarda la SetPixel.
Per eseguire i test uso questo codice:
DWORD t = GetTickCount();
Immagine img;
img.Create(5, 5);
for (int i = 0; i < 1000000; i++)
    img.SetPixel(0, 0, 255);
OutputElapsedTime(t);
OutputElapsedTime provvede ad eseguire la differenza di tempo fra il valore
restituito da GetTickCount() ed il t che ha ricevuto come parametro e
visualizza il risultato in una MessageBox.
vi ringrazio ancora, assa.


Ciao, assa!
Il giorno 21/10/2003 alle ore 13:31 hai scritto:
 a> Per eseguire i test uso questo codice:
 a>
 a> DWORD t = GetTickCount();
 a> Immagine img;
 a> img.Create(5, 5);
 a> for (int i = 0; i < 1000000; i++)
 a>     img.SetPixel(0, 0, 255);
 a> OutputElapsedTime(t);
Non so se quello che ha detto mfede si applica in questo caso (magari
rispondera' lui), ma vedo che imposti sempre lo stesso pixel e quindi accedi
sempre alla stessa zona di memoria che in lettura viene completamente messa
nella cache del processore, mentre in scrittura deve comunque accedere alla
ram. Tra l'altro usi un'immagine piccolissima (5x5 pixel se non sbaglio).
Ti consiglierei di provare in un altro modo, per esempio, creando
un'immagine piuttosto grande (che occupi almeno 1 megabyte in memoria) e
accedendo a tutti i pixel e non solo a uno, possibilmente non
sequenzialmente (sarebbe meglio un accesso random). Questo dovrebbe ridurre
gli effetti della cache e darti dei risultati piu' coerenti.
Comunque la mia opinione e' che in ogni caso la lettura sara' sempre piu'
veloce della scrittura.
Ah, se vuoi un timing piu' preciso, usa i timer multimediali con
timeBeginPeriod, timeEndPeriod e timeGetTime, puoi arrivare a una
risoluzione di 1 millisecondo (Windows permettendo).
Mi sembrava anche di ricordare che esiste un'opzione per generare il
sorgente assembler, se vuoi verificare che le funzioni siano effettivamente
inline, ma non ricordo piu' come si fa...
Bye

 OutputElapsedTime(t);
[cut]
azzardo un'ipotesi (ma c'è una certa probabilità che sbaglio): se chiami
GetPixel e non usi il valore da essa restituito, il compilatore ottimizza
(perché vede che la chiamata è inutile) e quindi tutto il for non lo
compila proprio (infatti il ritardo è 0, mentre anche solo per *contare*
fino a un miliardo, qualche secondo ci vuole, non so esattamente quanto);
se invece fai SetPixel il compilatore compila comunque la chiamata,
all'interno della quale si svolge del codice che non può essere omesso,
perché per quanto ne sa il compilatore potrebbe anche essere qualcosa di
importante.
Quindi prova a fare così:
DWORD t = GetTickCount();
Immagine img;
img.Create(5, 5);
DWORD dwResult;
for (int i = 0; i < 1000000; i++) {
    dwResult = img.GetPixel(0, 0, 255);
}
if (dwResult == 0) {
    dwResult = 0;
}
OutputElapsedTime(t);
in questo modo "inganni" il compilatore facendogli credere che il
risultato di GetPixel *non può* essere omesso perché è importante per lo
svolgimento successivo del codice:
if (dwResult == 0) {
    dwResult = 0;
}
in realtà questo if non fa niente, ma serve per ingannare il compilatore e
fargli credere che il programma finale *deve* controllare lo stato di
dwResult e reagire di conseguenza.
Se mi sbaglio su questa ipotesi, allora deve avere per forza ragione
1Sconosciuto.
ciao.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 inline, ma non ricordo piu' come si fa...
ti riferisci forse al blocco __asm ?


 rispondera' lui), [...]
Quello che intendevo dire e' che se *dentro* una funzione inline c'e' un
ciclo, allora MSVC non espande inline la funzione (non ricordo bene dove
l'ho letto).
Visto l'esempio di codice postato, quello che ho detto non si applica,
infatti e' la chiamata che e' dentro un ciclo.
Ciao. Federico.

Ciao, Al!
Il giorno 21/10/2003 alle ore 13:56 hai scritto:
 A> ti riferisci forse al blocco __asm ?
No proprio il sorgente asm che genera il compilatore.
Ho trovato l'opzione, si tratta del Listing Files nelle impostazioni del
sorgente da compilare, nel box Listing file type imposti Assembly Source
code e ti ritrovi anche il sorgente .asm che ti permette di verificare come
viene compilato il sorgente.
Bye

 1Sconosciuto.
molto probabilmente avete ragione entrambi, ho provato ad eseguire il tuo
codice ed effettivamente esegue molto lentamente rispetto a prima (sono 2600
ms circa per un miliardo di esecuzioni), ma comunque molto più velocemente
rispetto alla SetPixel (almeno dieci volte più veloce), e qui si avvalora
l'ipotesi della cache... non ho ancora fatto prove in quel senso, ma
provvederò al più presto.
inoltre mi sono rivisto un attimo la CreateDIBSection, con cui faccio
allocare lo spazio per la bitmap e non sono riuscito a capire se questo
spazio viene allocato sullo heap oppure su disco tramite FileMapping. voi ne
sapete qualcosa?
altra domanda: così per curiosità ho fatto la prova con le funzioni sin e
cos di math.h ed ho comunque visto che sono realmente molto veloci (non c'è
un ritardo apprezzabile nemmeno a un miliardo di iterazioni, ho preso gli
stessi accorgimenti che ho usato nella mia per "ingannare" il compilatore),
questo perchè non accedono mai alla memoria nè in lettura nè in scrittura? o
per qualche altro motivo?
grazie mille, assa.


 grazie mille, assa.
per calcolare una funzione trigonometrica non ci vuole niente, e in
particolar modo il tempo diminuisce quando usi float anziché double o long
double.
Mi lascia comunque perplesso però che con ben un miliardo di calcoli il
computer non si rallenti; non sarà tante volte un milione???
ciao
Al


 Ciao, Al!




non ricordo, o forse non la conoscevo proprio... però in fase debug so che
puoi controllare il codice assembly del tuo programma tramite la finestra
Disassembly (e non solo in VC++, so che anche la Borland ha la sua "CPU
Window").


 heap
oddio, non lo so; forse dipende dagli ultimi due parametri: se hSection č
NULL dwOffset perde senso e i bit vengono allocate nel free store,
altrimenti viene usato il File Mapping.
O almeno dovrebbe essere cosě, anche perché io in passato ho lavorato con
CreateDIBSection; per hSection passavo sempre NULL, e potevo accedere
all'array dei bits come se fossero stati in memoria (anche perché ogni
tanto facevo qualche pasticcio :P e causavo qualche errore di memoria...)


 memoria...)
non avendo mai provato a fare un file mapping non so... comunque credo che
alla fine la lettura/scrittura della memoria avvenga nello stasso modo che
se fosse ram, quindi con tutti gli errori del caso se si va a
leggere/scrivere fuori dai limiti, o sbaglio?
grazie, assa.


 leggere/scrivere fuori dai limiti, o sbaglio?
in tal caso credo che si tratti sempre di File Mapping, e comunque se
Windows ti da' l'impressione di lettura/scrittura diretta a te che ti
importa? Alla fine conta il risultato, no?
ciao
Al


 importa? Alla fine conta il risultato, no?
era sempre per un fatto di velocità, se si tratta di un file su disco ha
senso che la velocità in scrittura sia inferiore di quella in lettura, tutto
qui :)
grazie di tutto. ciao, assa.
