7 votes

Ce code peut-il être optimisé ?

J'ai un code de traitement d'image qui boucle sur deux tableaux d'octets multidimensionnels (de même taille). Il prend une valeur dans le tableau source, effectue un calcul dessus, puis stocke le résultat dans un autre tableau.

int xSize = ResultImageData.GetLength(0);
int ySize = ResultImageData.GetLength(1);

for (int x = 0; x < xSize; x++)
{                
   for (int y = 0; y < ySize; y++) 
   {                                                
      ResultImageData[x, y] = (byte)((CurrentImageData[x, y] * AlphaValue) +
                                    (AlphaImageData[x, y] * OneMinusAlphaValue));
   }
}

La boucle prend actuellement ~11ms, ce qui je suppose est principalement dû à l'accès aux valeurs des tableaux d'octets car le calcul est assez simple (2 multiplications et 1 addition).

Y a-t-il quelque chose que je puisse faire pour accélérer le processus ? C'est une partie critique de mon programme et ce code est appelé 80-100 fois par seconde, donc tout gain de vitesse, même minime, fera une différence. En outre, pour le moment, xSize = 768 et ySize = 576, mais cela augmentera à l'avenir.

Mise à jour : Merci à Guffa (voir réponse ci-dessous), le code suivant me fait gagner 4-5ms par boucle. Bien que ce soit non sécurisé code.

int size = ResultImageData.Length;
int counter = 0;
unsafe
{
    fixed (byte* r = ResultImageData, c = CurrentImageData, a = AlphaImageData)
    {
        while (size > 0)
        {
            *(r + counter) = (byte)(*(c + counter) * AlphaValue + 
                                    *(a + counter) * OneMinusAlphaValue);
            counter++;
            size--;
        }
    }
}

1voto

Les Points 447

Si CurrentImageData et/ou AlphaImageData ne changent pas à chaque fois que vous exécutez votre extrait de code, vous pourriez stocker le produit avant d'exécuter l'extrait de code que vous montrez et éviter cette multiplication dans vos boucles.

Edit : Une autre chose à laquelle je viens de penser : Parfois, les opérations sur les int sont plus rapides que celles sur les octets. Compensez cela par l'utilisation du cache de votre processeur (vous augmenterez considérablement la taille des données et le risque d'un échec de cache sera plus grand).

1voto

442 368 additions et 884 736 multiplications pour le calcul, je pense que 11 ms est en fait extrêmement lent sur un processeur moderne.

Bien que je ne connaisse pas bien les spécificités de .net, je sais que le calcul à grande vitesse n'est pas son point fort. Dans le passé, j'ai construit des applications Java avec des problèmes similaires, j'ai toujours utilisé des bibliothèques C pour faire le traitement de l'image / audio.

D'un point de vue matériel, vous devez vous assurer que les accès à la mémoire sont séquentiels, c'est-à-dire qu'il faut parcourir le tampon dans l'ordre où il se trouve en mémoire. Vous devrez peut-être aussi réorganiser tout cela de manière à ce que le compilateur tire parti des instructions disponibles telles que SIMD. La façon d'aborder cela dépendra de votre compilateur et je ne peux pas vous aider sur vs.net.

sur un DSP embarqué, je m'en sortirais.

(AlphaImageData[x, y] * OneMinusAlphaValue) et (CurrentImageData[x, y] * AlphaValue) et utiliser les instructions SIMD pour calculer les tampons, éventuellement en parallèle avant d'effectuer l'addition. Peut-être en faisant des morceaux assez petits pour garder les tampons en cache sur le cpu.

Je pense que tout ce que vous ferez nécessitera un accès plus direct à la mémoire/au processeur que ce que permet .net.

1voto

user51710 Points 641

Vous pouvez également jeter un coup d'œil au runtime Mono et à ses extensions Simd. Peut-être que certains de vos calculs peuvent utiliser l'accélération SSE, car je crois comprendre que vous faites essentiellement des calculs vectoriels (je ne sais pas jusqu'à quelle taille de vecteur il y a une accélération pour la multiplication, mais c'est le cas pour certaines tailles).

(Article de blog annonçant Mono.Simd : http://tirania.org/blog/archive/2008/Nov-03.html )

Bien sûr, cela ne fonctionnerait pas sur Microsoft .NET, mais vous êtes peut-être intéressé par une certaine expérimentation.

1voto

aronchick Points 2939

Il est intéressant de noter que les données des images sont souvent assez similaires, ce qui signifie que les calculs sont probablement très répétitifs. Avez-vous envisagé de créer une table de consultation pour les calculs ? Ainsi, à chaque fois que 0,8 est multiplié par 128 - valeur[80,128] que vous avez précalculé à 102,4, il vous suffit de le consulter ? En gros, vous échangez de l'espace mémoire contre la vitesse du processeur, mais cela pourrait fonctionner pour vous.

Bien sûr, si vos données d'image ont une résolution trop élevée (et vont jusqu'à un chiffre trop important), cela peut ne pas être pratique.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X