Newsgroup: it.comp.lang.c
Titolo: problema di ottimizzazione

Ciao a tutti,
sto cercando di migliorare un po' le performance di un mio applicativo
e mi sono trovato di fronte ad un comportamento inaspettato.
Il codice che ho scritto normalizza un vettore (a 3 componenti)
utilizzando la formuletta:
modulo = sqrt(x*x + y*y + z*z)
vettore normalizzato = {x/modulo, y/modulo, z/modulo}
pensavo di rosicchiare un po di tempo riscrivendo la formuletta sopra
in questo modo:
modulo_inv = 1/sqrt(x*x + y*y + z*z)
vettore normalizzato = {x*modulo_inv, y*modulo_inv, z*modulo_inv}
pensando che un prodotto fosse piu veloce di una divisione. Ma non è
cosi, anzi sembrerebbe che sia l'esatto opposto.
Di seguito l'output del mio test:
Utilizzando l'opzione O:
gcc -O -o testml testml.c -lm
Time fast: 30.320
Time slow: 18.900
-------------------------------------------------
Utilizzando l'opzione O2:
gcc -O2 -o testml testml.c -lm
Time fast: 0.760
Time slow: 0.720
-------------------------------------------------
Utilizzando l'opzione O3:
gcc -O3 -o testml testml.c -lm
Time fast: 0.720
Time slow: 0.720
-------------------------------------------------
'Time fast' è il tempo del ciclo che utilizza le coltiplicazioni, e
che pensavo migliore,
'Time slow' è il tempo relativo alle divisioni.
Il tempo di esecuzione converge utilizzando la massima ottimizzazione,
ma in tutti gli altri casi sembrerebbe che utilizzare le divisioni sia
più efficente.
Io però avrei giurato il contrario. Qualcuno mi sa spiegare questo
comportamento?
Grazie mille
   Alessio
Di seguito il caodice utilizzato.
questo è l'output del comando 'gcc -E testml.c' quindi le macro sono
risolte, come ad esempio 1000000l che nel sorgente è CLOCKS_PER_SEC.
  typedef float mfg_3dvector_t[3];
  mfg_3dvector_t vector;
  (vector)[0] = (11.3);
  (vector)[1] = (5.07);
  (vector)[2] = (7.15);
  clock_t start;
  float seconds = 0;
  unsigned int x;
  unsigned long int loop =  1000000000;
  float rNormal;
  start = clock();
  for(x=0;x<loop;x++)
    {
      rNormal = 1/sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
      (vector)[0] *= rNormal;
      (vector)[1] *= rNormal;
      (vector)[2] *= rNormal;
    }
  seconds = (float)(clock() - start) / 1000000l;
  printf("Time fast: %.3f\n", seconds);
  start = clock();
  for(x=0;x<loop;x++)
    {
      rNormal = sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
      (vector)[0] /= rNormal;
      (vector)[1] /= rNormal;
      (vector)[2] /= rNormal;
    }
  seconds = (float)(clock() - start) / 1000000l;
  printf("Time slow: %.3f\n", seconds);

    for(x=0;x<loop;x++)
[...]
Ciao, potresti postare il tuo sorgente? ci sono comunque delle inesat-
tezze. Ad esempio, in questo ciclo 'for' viene confrontato 'x' con
'loop', ma il primo č un _unsigned int_, il secondo _unsigned long int_.
Un unsigned int potrebbe poter rappresentare al massimo l'intero 65535,
nel qual caso sarebbe un loop infinito.
Si, in generale la moltiplicazione č sempre piů efficiente. Ma nel
tuo caso non stai moltiplicando e dividendo per lo stesso numero.
Dividi per 'modulo', ma moltiplichi per 'modulo_inv'. L'ottimizzazione
evita che 'modulo' e 'modulo_inv' siano ricalcolati ogni volta. Quindi
i tempi di esecuzione convergono.
-- 
"Non puoi insegnare qualcosa ad un uomo.
  Lo puoi solo aiutare a scoprirla dentro di sé." (G. Galilei)
Vincenzo Mercuri

, on 11/09/2010 12:12:05, 
wrote:

Scusa, ma da dove l'hai imparato il C? Utilizzi tonnellate di parentesi 
inutili!
A parte quello, non so cosa stia succedendo nel tuo compilatore, ma nel 
mio (MinGW 4.4.0) la versione con le moltiplicazioni è sempre più veloce 
di quella con le divisioni.
C'è da dire che non sono un esperto in materia di ottimizzazioni - e 
nemmeno di C - ma di sicuro il grosso del lavoro è la radice quadrata, e 
se ti informi potresti scoprire che magari non ne hai nemmeno bisogno.
A parte il problema indicato da Vincenzo, questa è la mia versione del 
tuo programma, e anche io sarei curioso di vedere esattamente che codice 
hai compilato (dato che il tuo, così com'è, non dovrebbe nemmeno compilare):
//-------
#include <stdio.h>
#include <time.h>
#include <math.h>
int main(void) {
     float vector[3];
     vector[0] = 11.3;
     vector[1] = 5.07;
     vector[2] = 7.15;
     clock_t start;
     float seconds = 0;
     unsigned long x;
     unsigned long loop =  100000000;
     float rNormal;
     start = clock();
     for (x = 0; x < loop; ++x) {
         rNormal = 1/sqrt((float)(vector[0]*vector[0]
                                  + vector[1]*vector[1]
                                  + vector[2]*vector[2]));
         vector[0] *= rNormal;
         vector[1] *= rNormal;
         vector[2] *= rNormal;
     }
     seconds = (float)(clock() - start) / CLOCKS_PER_SEC;
     printf("Moltiplicazione: %.3f\n", seconds);
     start = clock();
     for (x = 0; x < loop; ++x) {
         rNormal = sqrt((float)(vector[0]*vector[0]
                                + vector[1]*vector[1]
                                + vector[2]*vector[2]));
         vector[0] /= rNormal;
         vector[1] /= rNormal;
         vector[2] /= rNormal;
     }
     seconds = (float)(clock() - start) / CLOCKS_PER_SEC;
     printf("Divisione: %.3f\n", seconds);
     return 0;
}
/*
senza ottimizzazioni
Moltiplicazione: 5.926
Divisione: 15.354
con  ottimizzazioni
Moltiplicazione: 5.054
Divisione: 6.577
*/
//-------
-- 
  FSC - 
http://userscripts.org/scripts/show/59948
 
http://fscode.altervista.org
 - 
http://sardinias.com

 compilare):
Già...poi sarebbe più utile avere il sorgente non preprocessato.
Non è molto utile postare l'output di gcc -E... anzi, si tratta
di pagine e pagine di output del preprocessore. E comunque quello
che l'OP ha postato non è neanche il sorgente preprocessato,
ma, appunto come hai detto, solo un frammento non compilabile...




Ad esempio, hai la possibilità di velocizzare il calcolo della radice 
quadrata inversa rubando una funzione da Quake - vedi l'esempio qua 
sotto e nota come i risultati siano lievemente falsati, ma nota pure che 
eseguendo un'altra iterazione l'approssimazione migliora parecchio, 
rimuovi il commento e misura le prestazioni, che ovviamente 
peggioreranno, ma dovrebbero sempre essere migliori del calcolo diretto 
della radice quadrata.
Per fare un altro esempio, un caso nel quale non hai assolutamente 
bisogno della radice quadrata è durante il confronto di due vettori: se 
la lunghezza di un vettore è inferiore a quella di un altro, allora 
anche il suo quadrato sarà inferiore a quello di un altro, quindi tanto 
vale non estrarre le radici quadrate e confrontare direttamente i 
quadrati ;-)
//-------
#include <stdio.h>
#include <time.h>
#include <math.h>
/*
     funzione che calcola la radice quadrata inversa
     tramite il metodo iterativo di Newton a partire
     da una costante magica - funzione "rubata" da Quake
*/
float inv_sqrt( float number ) {
     long i;
     float x2, y;
     const float threehalfs = 1.5F;
     x2 = number * 0.5F;
     y  = number;
     i  = * ( long * ) &y;
     i  = 0x5f3759df - ( i >> 1 );
     y  = * ( float * ) &i;
     // prima iterazione
     y  = y * ( threehalfs - ( x2 * y * y ) );
     // seconda iterazione (non utilizzata in Quake!!!)
     // y  = y * ( threehalfs - ( x2 * y * y ) );
     return y;
}
int main(void) {
     clock_t start;
     float seconds = 0;
     unsigned long x;
     unsigned long loop =  100000000;
     float rNormal;
     {
         float vector[3] = {11.3, 5.07, 7.15};
         printf("%.3f %.3f %.3f\n", vector[0], vector[1], vector[2]);
         start = clock();
         for (x = 0; x < loop; ++x) {
             rNormal = inv_sqrt((float)(vector[0]*vector[0]
                                        + vector[1]*vector[1]
                                        + vector[2]*vector[2]));
             vector[0] *= rNormal;
             vector[1] *= rNormal;
             vector[2] *= rNormal;
         }
         seconds = (float)(clock() - start) / CLOCKS_PER_SEC;
         printf("usando inv_sqrt: %.3f\n", seconds);
         printf("%.3f %.3f %.3f\n", vector[0], vector[1], vector[2]);
     }
     puts("---");
     {
         float vector[3] = {11.3, 5.07, 7.15};
         printf("%.3f %.3f %.3f\n", vector[0], vector[1], vector[2]);
         start = clock();
         for (x = 0; x < loop; ++x) {
             rNormal = 1/sqrt((float)(vector[0]*vector[0]
                                      + vector[1]*vector[1]
                                      + vector[2]*vector[2]));
             vector[0] *= rNormal;
             vector[1] *= rNormal;
             vector[2] *= rNormal;
         }
         seconds = (float)(clock() - start) / CLOCKS_PER_SEC;
         printf("usando 1/sqrt: %.3f\n", seconds);
         printf("%.3f %.3f %.3f\n", vector[0], vector[1], vector[2]);
     }
     return 0;
}
/*
11.300 5.070 7.150
usando inv_sqrt: 3.799
0.789 0.354 0.499
---
11.300 5.070 7.150
usando 1/sqrt: 4.962
0.790 0.355 0.500



Ah, è per quello che CLOCKS_PER_SECOND non appariva, e appariva invece 
la costante numerica? Non è che anche tutte quelle parentesi balorde le 
aveva aggiunte il compilatore dopo la fase di preprocessamento?


 aveva aggiunte il compilatore dopo la fase di preprocessamento?
si, CLOCKS_PER_SECOND non compariva per questo motivo, tuttavia
la mia versione di gcc non aggiunge tutte quelle parentesi dopo
il preprocessamento del tuo sorgente...quindi chissà...


 il preprocessamento del tuo sorgente...quindi chissà...
Eh, per le parentesi allora mi sa che ci avevo visto giusto, deve averle 
messe l'OP nel codice originale ;-)
Beh, non mi resta che aspettare conferma o smentita - e in caso di 
smentita, sarebbe carino sapere la versione esatta del compilatore... OP?


 messe l'OP nel codice originale ;-)
Eheh, pare proprio di si :-)




Errore, volevo dire che eseguendo un'altra iterazione l'approssimazione 
_non_ migliora parecchio - d'altra parte, ci sarà pure un motivo se in 
Quake si accontentano della prima iterazione: l'approssimazione è già 
molto buona!


 i tempi di esecuzione convergono.
Questa parte...pur essendo vera, comunque non è fondamentale. In realtà
ai fini delle performance bisogna considerare più i tipi che non i dati.
Si parla di (float * float) più efficiente di (float / float), non tanto
dei dati moltiplicati o divisi (questo è un problema marginale alla
luce dei compilatori moderni).
Poi per quanto riguarda l'ottimizzazione...il loop-unrolling in questo
caso gioca il ruolo più importante, visto il numero di cicli e il tipo 
di loop.


 Ciao e grazie per le risposte, faccio un po di chiarezza  sul codice.
Il mio sorgente originare contiene molte macro e quindi ho pensato che
postarlo esattamente com'era sarebbe risultato incombrensibile. Le
parentesi sono conenute nelle macro con argomenti, quando scrivo una
macro solitamente utilizzo sempre i parametri tra parentesi, non
peggiorano le performance ma mi evitano diversi problemi. Ma questo è
un alto discorso. Il mio obbiettivo era quello di far capire le mie
intenzioni, ma in realtà ho creato un po di confusione. Ora ho postato
il sorgente esatto, risolvendo le macro e anche le inclusioni dei
file .h che contengono le macro. Alcune parentesi sono rimaste ma non
credo che possano influenzare i tempi.
Il motivo che mi spinge a normalizzare è che devo calcolare la normale
del piano su cui giace un poligono. Ma a parte la necessità o meno di
utilizzare  sqrt o eventuali miglioramenti al codice (ho apprezzato
molto la funzione per il calcolo della radice quadrata), quello che mi
ha spinto a scrivere è stato il fatto che se eseguo il mio programma
di test senza ottimizzazioni la versione con le moltiplicazioni ha
tempi quasi doppi rispetto a quella con le divisioni. E' vero che il
codice per il monitoraggio dei tempi non è certo il migliore ed è
influenzato da diversi fattori. Rimane il fatto che a questo punto non
so spiegarne il comportamento, e questo mi crea parecchi dubbi su come
affrontare problemi di questo tipo. Non sono certo un esperto, la
computer grafica è solo un divertimento, ma perchè lo sia è essenziale
che capisca i meccanismi. Ed ora ho  delle perplessità. Ho commesso
degli errori nel mio programma di test? o ci sono delle dinamiche
particolari a livello di codice macchine che dovrei valutare?
Ho rieseguito i test con il sorgente ed i tempi sono rimasti gli
stessi. Le moltiplicazioni sembrano prendere piu tempo alla cpu.
So che quello che sto per dire è ovvio, è solo una curiosità, ma
modificando i valori delle componenti del vettore ottengo tempi molto
differenti.
As esempio se modifico i valori delle componenti del vettore:
[...]
  (vector)[0] = (11.3);
  (vector)[1] = (5.07);
  (vector)[2] = (7.05);
[...]
Ho modificato solo il terzo valore, allora dimezzo i tempi relativi
alla moltiplicazione, pur restando superiori:
Time slow: 14.980 <- divisioni
Time fast: 16.050 <- moltiplicazioni
Grazie ancora.
   Alessio
Il sorgente compila e queste sono le informazioni sul compilatore che
sto utilizzando:
$ gcc -v
Using built-in specs.
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu
4.4.3-4ubuntu5' --with-bugurl=file:///usr/share/doc/gcc-4.4/
README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/
usr --enable-shared --enable-multiarch --enable-linker-build-id --with-
system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-
threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --program-
suffix=-4.4 --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug
--enable-plugin --enable-objc-gc --disable-werror --with-arch-32=i486
--with-tune=generic --enable-checking=release --build=x86_64-linux-gnu
--host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5)
#include <time.h>
#include "math.h"
#include "stdio.h"
typedef float mfg_3dvector_t[3];
int main(void)
{
  mfg_3dvector_t vector;
  mfg_3dvector_t vector2;
  (vector)[0] = (11.3);
  (vector)[1] = (5.07);
  (vector)[2] = (7.15);
  clock_t start;
  float seconds = 0;
  unsigned long int x;
  start = clock();
  unsigned long int loop =  1000000000;
  float rNormal;
 start = clock();
  for(x=0;x<loop;x++)
    {
      rNormal = sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
      (vector)[0] /= rNormal;
      (vector)[1] /= rNormal;
      (vector)[2] /= rNormal;
    }
  seconds = (float)(clock() - start) / 1000000l;
  printf("Time slow: %.3f\n", seconds);
 start = clock();
  for(x=0;x<loop;x++)
    {
      rNormal = 1/sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
      (vector)[0] *= rNormal;
      (vector)[1] *= rNormal;
      (vector)[2] *= rNormal;
    }
  seconds = (float)(clock() - start) / 1000000l;
  printf("Time fast: %.3f\n", seconds);
  return 0;
}

 inutili!
Probabilmente nello stesso luogo dove tu hai imparato a quotare, vedi 
anche la replica da parte tua al tuo stesso messaggio.
Ognuno mette le parentesi dove gli pare, l'importante è che per chi 
scrive sia chiaro il significato.

 scrive sia chiaro il significato.
Se avessi seguito la discussione avresti scoperto - come ho scoperto io 
- che quelle parentesi erano nelle macro che avevano generato quel 
codice, e quindi erano non "a piacere", ma addirittura "necessarie".
Se proprio devi infilarti nelle discussioni altrui per correggere, fallo 
con criterio e completezza!



Interessante... conta però che il metodo utilizzato da Quake è 
particolare per la costante magica utilizzata per calcolare il primo 
tentativo - se ho capito bene, l'utilizzo di tale costante in 
particolare diminuisce il numero di iterazioni necessario ad ottenere 
una buona approssimazione - questo metodo non è contemplato dalla pagina 
che hai citato e sarebbe interessante mettere a confronto i vari 
approcci, riguardo a precisione e prestazioni.


 Time fast: 16.050<- moltiplicazioni
Mi sta venendo il dubbio che il problema non sia nel tuo codice o nel 
compilatore, ma proprio nel sistema fisico che stai utilizzando.
Ho rimpiazzato la costante numerica con CLOCKS_PER_SECOND nel tuo ultimo 
codice, l'ho compilato e fatto girare, e questi sono i risultati:
Time slow: 66.866
Time fast: 49.055
La moltiplicazione resta la più veloce nel mio sistema, usando il tuo test.
Un'altra nota che devo fare è che, al momento della prima iterazione, i 
valori contenuti dal vettore passano subito ai valori normalizzati, 
quindi il tuo loop sta eseguendo sempre gli stessi calcoli a prescindere 
dal valore iniziale (in altre parole, il primo loop tira fuori una 
radice molto diversa da uno, ma poi stai sempre calcolando una radice 
intorno ad uno e stai moltiplicando i valori del vettore sempre per quel 
valore intorno ad uno).
Spero di esserti stato d'aiuto in qualche modo!


 Ho rimpiazzato la costante numerica con CLOCKS_PER_SECOND
Va be', con CLOCKS_PER_SEC, ovviamente!


 
http://medialab.di.unipi.it/web/IUM/Fondamenti/cap4.htm
Mi riferivo all'algoritmo di Bresenham, ovviamente, ma nella pagina ci 
sono un bel po' di altre cose interessanti.


cosi, anzi sembrerebbe che sia l'esatto opposto.
Molto banalmente... ma calcolando il reciproco non fai un passo in più?
Sempre nell'ipotesi che moltiplicazione e divisione impieghino lo stesso 
tempo ad essere eseguite.
.. 

 ha scritto nel messaggionews:395aa831-a456-4f6c...@t11g2000vbc.googlegroups.com...

Ciao,
credo di aver fatto qualche passo avanti, quindi mi sembrava giusto
condividere a tutti quello che ipotizzo sia successo.
Come ha giustamente ha evidenziato Francesco dopo la prima iterazione
il mio vettore risulta essere già normalizzato, quindi tutti i calcoli
successivi ( radice quadrata, moltiplicazioni e divisioni ) sono fatti
utilizzando il valore 1, modulo di un vettore normalizzato. In queste
condizioni particolari 1/sqrt() fa la differenza. Ho provato a
riscrivere il mio programma facendo in modo di non cambiare i
componenti del vettore, cosi facendo ottengo che il ciclo con le
moltiplicazioni è piu veloce di 5 secondi rispetto alle divisioni.
Nel caso in cui il modulo M del vettore è 1,  1/sqrt(M) pesa molto di
piu della differenza di prestazioni tra moltiplicazioni e divisioni
per lo stesso modulo.
La stranezza nei tempi era quindi dovuta al mio caso di test che
genera un condizione particolare e non ricrea le condizioni generali.
Grazie a tutti per i preziosi consigli.
   Alessio

   start = clock();
Qui il primo start = clock() è superfluo.


 efficienza di 1/sqrt rispetto al semplice calcolo di sqrt??
Grazie per le osservazioni.
In effetti avevo già visto che 1/sqrt() e molto pesante, e
affievolisce il vantaggio delle moltiplicazioni rispetto alle
divisioni. E mi sembra di aver capito che questo dipende anche dal
fatto particolare che i vettori risultanao normalizzati dopo il primo
ciclo.
Ho provato ad eseguire il codice che hai postato e con sorpresa il
sorgente TESTfinale.c ha evidenziato che le moltiplicazioni sono piu
efficenti. E mi è sembrato molto strano, visto che in tutti i miei
test avevo ottenuto altri valori.
Ho confrontato il mio codice con il tuo e ho notato questo: io non
casto l'output della funzione sqrt().
Mio codice:
rNormal = 1/sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
Tuo codice:
rNormal = 1.f / (float) sqrt((float) (vector[0] * vector[0] +
                                           vector[1] * vector[1] +
                                           vector[2] * vector[2]));
Nel mio codice senza cast le moltiplicazioni sono piu lente me mi
basta aggiustare la riga in questo modo:
rNormal = 1.f/(float)sqrt( (float)((vector)[0]*(vector)[0] + (vector)
[1]*(vector)[1] + (vector)[2]*(vector)[2] ) );
e impsovvisamente ho una situazione ribaltata:
Time mult: 2.860
Time div: 3.280
Ho fatto anche la controprova, ossia ho tolto il cast dal tuo codice
ed in effetti ottengo questo risultato:
vector[0] = 11.300000
vector[1] = 5.070000
vector[2] = 7.150000
Moltiplicazione: 3.360000
vector[0] = 0.790157
vector[1] = 0.354522
vector[2] = 0.499967
Divisione: 3.280000
vector[0] = 0.790157
vector[1] = 0.354522
vector[2] = 0.499967
Ora è evidente che il cast fa la differenza. Non so ancora il perchè
però è sicuramente la causa dell'anomalia sui tempi.
Grazie ancora.
   Alessio

 e impsovvisamente ho una situazione ribaltata:
Allora, si, hai ragione a evidenziare questo fatto. La spiegazione non
è difficile. Come prima cosa però vorrei far notare una cosa:
il cast sugli argomenti passati a 'sqrt' in effetti è completamente
superfluo. Il motivo è che 'castiamo' a float dei float, perchè tali
sono i vector[i]...e ha poco senso fare un cast di un tipo al suo
stesso tipo. La 'sqrt' opera su double. Quindi, con o senza cast sugli
argomenti, questi ultimi vengono trasformati in double, come in un
assegnamento, e poi vengono elaborati da sqrt che _restituisce un double_.
Detto questo, è importante che tu abbia già chiaro un concetto:
in un'operazione aritmetica, quando un operando è double e l'altro
float (o int), allora il dato di tipo float (o int) viene 'promosso'
automaticamente a double. Quando un tipo è float e l'altro int,
quest'ultimo viene promosso a float. Facile.
Quindi ritorniamo alla funzione 'sqrt' e ricordiamo che essa
restituisce un double. Potremmo per esempio scrivere allora:
     double m = sqrt(vector[0] * vector[0] +
                     vector[1] * vector[1] +
                     vector[2] * vector[2]);
dove ho tagliato via il cast sugli argomenti.
Ora, il tuo codice fa una cosa del genere:
     float rNormal = 1 / m;
qui sta il problema:
     1  è una costante di tipo _int_
     m  è di tipo _double_
quindi 1 viene promosso a double e l'operazione "/" diventa
_divisione tra double_. Il risultato viene poi memorizzato in rNormal
con conseguente troncamento (passaggio da double a float).
Se invece scrivo:
     float rNormal = 1 / (float) m;
succede che:
     1  è una costante di tipo _int_
     m  è di tipo _float_ (double troncato)
quindi 1 viene promosso a float e l'operazione "/" diventa
_divisione tra float_. Il risultato viene memorizzato così com'è
in rNormal, che è anch'essa float.
Nel caso:
     float rNormal = 1.f / m;
     1.f  è di tipo _float_
     m    è di tipo _double_
pertanto questa è ancora una _divisione tra double_.
Nel mio codice invece:
     float rNormal = 1.f / (float) m;
     1.f  è di tipo _float_
     m    p di tipo _float_
quindi non vengono effettuate promozioni e "/" è
una _divisione tra float_. Riepilogando:
tuo codice:    float rNormal = 1 / m;
(promozione di 1 a double, divisione double)
mio codice:    float rNormal = 1.f / (float) m;
(divisione float)
Ed essendo le divisioni tra double più 'costose' di quelle
tra divisioni float, il secondo modo risulta più performante.
--------------------------------------------------------------------
Apro una parentesi a proposito di un dettaglio che un po' tutti
abbiamo tralasciato:
fra un ciclo e l'altro ci siamo dimenticati di riportare i vector[i] ai
valori iniziali (dopo il primo ciclo i vettori sono già normalizzati):
quindi, fra un ciclo e l'altro:
   vector[0] = 11.3f;
   vector[1] = 5.07f;
   vector[2] = 7.15f;
--------------------------------------------------------------------
Altra parentesi: lo Standard C99 mette a disposizione anche la versione
_sqrtf_ di sqrt, che quindi prende e restituisce float. Tuttavia,
contrariamente alle mie aspettative, le performance (sulla mia macchina)
non sono migliori rispetto alla classica 'sqrt'. Una breve ricerca su
internet mi ha fatto capire che è probabile che sqrtf usi algoritmi
diversi da sqrt, non solo per il calcolo della radice, ma anche
per la gestione e il controllo degli errori. Peccato!
--------------------------------------------------------------------
Altro suggerimento:
potresti trovare qualche utile algoritmo qui:
http://www.nrbook.com/a/bookcpdf.php
si tratta della seconda edizione del libro "Numerical Recipes in C",
disponibile gratuitamente online, libro molto popolare soprattutto
nella comunità dei programmatori di software scientifico.


 m p di tipo _float_
naturalmente mi č scappata una 'p' che doveva essere una 'č'


 il cast sugli argomenti passati a 'sqrt' in effetti è completamente
sull'argomentO, ce n'è soltanto uno. (ho parlato di argomenti al plurale
anche dopo, pardon)



Giusto un commento, per riportare la discussione al 2010: per questo
tipo di conti (sqrt/floating point) sui pc si usano SSE o GPU.
Ciao!



Ovviamente quando è possibile avvantaggiarsi del metallo molte 
ottimizzazioni passate diventano superflue e obsolete, ma non è sempre 
questo il caso e quindi credo sia importante segnalarle in caso non se 
ne conosca l'esistenza - conta che non ho approfondito il discorso qui 
perché non l'ho approfondito per me stesso in primo luogo: quando mi 
sono trovato ad averne bisogno, invece di indagare sul metallo ho 
trovato una ottimizzazione sufficiente alle mie necessità nonostante 
fosse, a quanto pare, obsoleta. Mi riferisco ad un caso specifico: ho 
implementato l'algoritmo di Bresenham sulle primitive della libreria SDL 
per velocizzare un programmino senza stare lì a scomodare OpenGL, 
DirectX o quant'altro, dato che il livello di competenza necessario a 
padroneggiarne interfaccia e setup era troppo alto rispetto a quello 
necessario ad implementare alcune semplici primitive grafiche come linee 
inclinate e cerchi, che erano tutto quello che mi serviva.
