J'ai écrit un algorithme pour convertir une image RVB en Yuv420. Je passe beaucoup de temps à essayer de le rendre plus rapide, mais je n'ai trouvé aucune autre façon de renforcer son efficacité, alors maintenant je me tourne vers vous pour que vous puissiez me dire si cela est aussi bon que je reçois, ou s'il y a une autre plus efficace façon de le faire (l'algorithme est en C ++ mais c et assembleur sont également des options)
5 Réponses :
Vous pouvez utiliser SSE ou 3DNOW Codes d'assemblage afin de renforcer les performances. P>
Quant au code C ++, je pense qu'il est difficile d'améliorer en fonction de votre code actuel. P>
Pour que mes capacités d'assembleur soient réduites pour comprendre le code, mais je ne me considère pas particulièrement bien l'écrire.
Le seul point évident que je peux voir est que vous faites ... devient: p> .. bien que je doute que cela aurait beaucoup d'impact. P> Comme le suggère Ciphor, je pense que l'assemblage est la seule façon de vous améliorer sur ce que vous avez arrivé là-bas. P> P> 3 * i code> trois fois. Vous pourriez stocker ce résultat dans une variable locale, mais le compilateur peut déjà le faire. Donc ..
Tout compilateur raisonnable le fera pour vous. Et les bons verront que chacun de ces indicateurs est incrémenté par 3 chaque étape, et va juste +3 eux (ou trouver une manière encore meilleure que je ne puisse pas penser à l'heure actuelle!)
Intéressant assez, j'avais essayé cela avec une pénalité de performance ... mais je viens de découvrir que je n'avais pas toutes l'optimisation du compilateur au maximum
n'accédez pas à des pointeurs plus une fois, copiez la valeur sur la pile, puis utilisez la valeur sur la pile. (Aliasing)
... int v_r = *r; int v_g = *g; int v_b = *b; *y = ((lookup66[v_r] + lookup129[v_g] + lookup25[v_b]) >> 8) + 16; ...
Je suppose que les tables de recherche sont superflues. Les multiplications respectives doivent être plus rapides qu'un accès à la mémoire. Surtout dans une telle boucle interne.
Alors, j'appliquerais également de petits changements (comme d'autres que d'autres ont déjà suggéré) ..: p> edit Strong> P> Vous devez également réorganiser le code, de sorte que vous puissiez supprimer le si () code>. Petites boucles internes simples sans branches sont rapides. Ici, c'est peut-être une bonne idée de d'abord écrire votre avion, puis des avions U et V, comme celui-ci: p>
C'était ma première version et est effectivement plus rapide en mode de débogage, mais pas en version de version.
D'accord, l'utilisation de tables de recherche pour la multiplication est une perte nette.
Si vous trouvez des tables de recherche plus rapidement que la multiplication, je n'ai vraiment aucune idée de quoi faire de cela. Optimisation de la manivelle et voyez si c'est toujours vrai ...
tables où encore plus vite, mais en prenant l'intérieur si le tour
Pour le compte rendu, combien plus vite a-t-il couru?
Il faisait en moyenne 4,460 ms et a sauté à 1,445 ms
Avez-vous une fonction inverse correspondante?
dérouler votre boucle et éliminez le si dans la boucle intérieure. Mais ne fonctionne pas sur les données d'image 3 fois, et il est encore plus rapide!
void Bitmap2Yuv420p_calc2(uint8_t *destination, uint8_t *rgb, size_t width, size_t height) { size_t image_size = width * height; size_t upos = image_size; size_t vpos = upos + upos / 4; size_t i = 0; for( size_t line = 0; line < height; ++line ) { if( !(line % 2) ) { for( size_t x = 0; x < width; x += 2 ) { uint8_t r = rgb[3 * i]; uint8_t g = rgb[3 * i + 1]; uint8_t b = rgb[3 * i + 2]; destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; destination[upos++] = ((-38*r + -74*g + 112*b) >> 8) + 128; destination[vpos++] = ((112*r + -94*g + -18*b) >> 8) + 128; r = rgb[3 * i]; g = rgb[3 * i + 1]; b = rgb[3 * i + 2]; destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; } } else { for( size_t x = 0; x < width; x += 1 ) { uint8_t r = rgb[3 * i]; uint8_t g = rgb[3 * i + 1]; uint8_t b = rgb[3 * i + 2]; destination[i++] = ((66*r + 129*g + 25*b) >> 8) + 16; } } } }
Avez-vous testé le résultat? Parce que je ne pense pas cela pour: pour (taille_t x = 0; x
Je n'ai pas comparé les résultats, mais la destination [i ++] est écrite deux fois par itération, de sorte que ce n'est pas une préoccupation de mon point de vue. I est l'indice source et l'indice de destination pour l'avion Y.
Au fait, si vous vous souciez de la qualité de votre image de sortie, vous devez penser que vous modifiez votre algorithme en moyenne les valeurs R / G / B de chaque bloc de pixel 2x2 avant de la convertir en U / V, au lieu de simplement en choisir un Échantillon R / G / B de chaque bloc 2x2. Au moins c'est comme ça que je comprends le codage Y420.
Eh bien, vous aviez raison, cela a parcouru quelques centaines de microsecondes plus rapidement que la réponse précédemment acceptée, jusqu'à 1.1MS
+1! À première vue, je ne pouvais pas comprendre comment écrire à trois emplacements de mémoire lointains (Y, U & V Planes) pourrait être tant plus rapide (comparé à ma réponse). Maintenant, je sais: ça (probablement !?) est dû à la technique de combinaison d'écriture dans les processeurs modernes x86 ( mécanique-sympathy.blogspot.com/2011/07/write-comminant.ht ml ).
@Frunsi je pense que vous seriez, que vous exécutez 3 fois sur les données d'entrée pour générer la sortie et la solution de Timbo ne le fait qu'une seule fois.
Avez-vous une fonction inverse correspondante?
Eh bien, j'ai itérisé sur cet algorithme, de ne pas avoir de tables de recherche et d'utiliser des variables temporaires à cela.
Je sens ta douleur que je devais faire face au coût de cette conversion aussi ...