2009-09-02 7 views
3

J'ai la formule suivanteOptimisation d'une division à virgule flottante et le fonctionnement conversion

float mean = (r+b+g)/3/255.0f; 

Je veux accélérer. Il y a les conditions suivantes

0<= mean <= 1 and 0 <= r,g,b <= 255 and r, g, b are unsigned chars 

donc si j'essaie d'utiliser le fait que >> 8 est comme divisant par 256 et j'utiliser quelque chose comme

float mean = (float)(((r+b+g)/3) >> 8); 

ce retournera toujours 0. Est-il façon de passer la division des flotteurs coûteux et se retrouver avec une moyenne entre 0 et 1?

Répondre

16

pré-convertir vos divisions en une constante Multipliable:

a/3/255 

est le même que

a * (1/(3 * 255)) 

si précalculer:

const float AVERAGE_SCALE_FACTOR = 1.f/(3.f * 255.f) 

puis juste faire

float mean = (r + g + b) * AVERAGE_SCALE_FACTOR; 

puisque la multiplication est généralement beaucoup plus rapide que la division.

+1

+1 pour une solution qui conduira probablement à une accélération et peut être implémentée avec élégance. Cependant, il vaut la peine de mesurer pour s'assurer que c'est vraiment plus rapide. – sharptooth

+0

bonne accélération merci, dommage que je ne peux pas utiliser l'astuce de décalage serait encore plus rapide je soupçonne :( – ldog

+2

Si votre compilateur est l'optimisation du tout, la multiplication devrait être bien.Le truc de décalage ne fonctionnera probablement pas pour les flotteurs. –

8

vous comparez évidemment la moyenne à quelque chose d'autre, qui est également entre 0 et 1. que diriez-vous simplement multiplier ce quelque chose par 255 à la place?

+0

Malheureusement, je ne peux pas sauter la division. La normalisation est obligatoire car elle doit finalement se produire de toute façon pour une corrélation croisée normalisée – ldog

+0

+1 pour la solution pragmatique. –

5

Laisse-nous savoir ce que fait réellement un vrai compilateur avec ce code? J'aime MINGW GCC 4.3 (x86). Je "test.c gcc -c -O2 -S -Wall"

Cette fonction:.

float calc_mean(unsigned char r, unsigned char g, unsigned char b) 
{ 
    return (r+b+g)/3/255.0f; 
}

génère ce code objet (entrée de la fonction et le code de sortie pour plus de clarté j'espère que les commentaires que j'ai ajouté sont à peu près correct):

 
movzbl 12(%ebp), %edx ; edx = g 
movzbl 8(%ebp), %eax  ; eax = r 
addl %eax, %edx  ; edx = eax + edx 
movzbl 16(%ebp), %eax ; eax = b 
addl %eax, %edx  ; edx = eax + edx 
movl $1431655766, %eax ; 
imull %edx    ; edx *= a const 
flds LC0    ; put a const in the floating point reg 
pushl %edx    ; put edx on the stack 
fidivrl (%esp)   ; float reg /= top of stack 

considérant que cette fonction:

float calc_mean2(unsigned char r, unsigned char g, unsigned char b) 
{ 
    const float AVERAGE_SCALE_FACTOR = 1.f/(3.f * 255.f); 
    return (r+b+g) * AVERAGE_SCALE_FACTOR; 
} 

génère ceci:

 
movzbl 12(%ebp), %eax  
movzbl 8(%ebp), %edx 
addl %edx, %eax 
movzbl 16(%ebp), %edx 
addl %edx, %eax 
flds LC2 
pushl %eax 
fimull (%esp) 

Comme vous pouvez le voir, la deuxième fonction est meilleure. Compiler avec -freciprocal-math convertit le fidivrl de la première fonction en un fimull, ce qui devrait être une amélioration. Mais la deuxième fonction est encore meilleure. Cependant, si vous considérez qu'une CPU de bureau moderne a quelque chose comme un pipeline à 18 étages et qu'elle est capable d'exécuter plusieurs de ces instructions par cycle, vous pouvez voir que la performance de ces fonctions sera dominée par des stalles dues aux dépendances de données. J'espère que votre programme a cet extrait de code en ligne et avec un peu de déroulement de la boucle. Considérer qu'un si petit fragment de code isolé n'est pas idéal. C'est un peu comme conduire une voiture avec des jumelles collées aux orbites. Zoom sur l'homme!

1

Comme le montre Andrew, la fonction d'origine n'est pas optimisée du tout. Le compilateur n'a pas pu parce que vous divisiez la somme d'abord par un entier, puis par un flottant. Ce n'est pas la même chose que multiplier par le facteur d'échelle moyenne mentionné ci-dessus. Si vous changez (r + g + b) /3/255.0f en (r + g + b) /3.0f/255.0f, le compilateur peut l'optimiser pour utiliser automatiquement fimull.

+0

+1 Oui, ils ne sont pas identiques: http://codepad.org/0efh0w19 –

1

Il est très courant d'optimiser ces opérations pour une plate-forme, plutôt que comme un algorithme ou un C portable. Le Virtual Dub blog vaut la peine d'être lu pour des conseils sur la façon dont cela est fait dans un logiciel destiné aux architectures x86 et x64. quelques entrées sur l'optimisation des moyennes de pixels 12.

Questions connexes