8
votes

Manière la plus performante de soustraire un tableau d'un autre

J'ai le code suivant qui est le goulot d'étranglement dans une partie de mon application. Tout ce que je fais, c'est soustraire le tableau d'un autre. Ces deux tableaux ont plus d'environ 100 000 éléments. J'essaie de trouver un moyen de rendre cela plus performant. XXX

Est-ce que quelqu'un a une suggestion?


12 commentaires

Bonne chance d'aller mieux que ça!


Peut-être que certaines boucles déroulantes, mais je ne sais pas mieux en ce moment.


De simples soustractions entier de 100 000 entières doivent être très rapides. Je suppose que vous exécutez ce code dans une boucle serrée ou quelque chose comme vous le sentez que c'est un goulot d'étranglement. @ La solution de David est la seule, alors. (Bien sûr, bien sûr que s'il existe un moyen de se débarrasser de la soustraction de la matrice, mais c'est une autre question.)


( $ R - ) pour désactiver la plage de la plage au moins sur la boucle. Peut-être aussi sur le code ci-dessus remplissant les tableaux si vous gérez soigneusement la gamme des sous-domes ...


Ah, retour au {$ r -} débat!


Je dois essayer de désactiver la vérification de la plage demain. Merci pour le conseil.


Cela pourrait être une bonne question à poser sur Refactormycode.com/tags/delphi


Dec (Array1 [IX], Array2 [IX]) Au lieu de Array1 [IX]: = Array1 [IX] - Array2 [IX] . Je ne sais pas si cela fonctionnera plus vite, mais vous pouvez probablement le taper plus vite, donc à certains égards, c'est une manière plus performante.


Hiroyuki Hori avait des trucs MMX pour Delphi: TORRY.NET/AUTHORSMORE.PHP?ID=1181


@Andriy qui ne fonctionne pas plus vite. Vous seriez déçu du compilateur si c'est le cas!


@David: Tout comme je soupçonnais. Nous ne sommes donc laissés que le stimulation de performance presque inexistante de la frappe moins de frappe alors.


@Worm Corriguez: Xmm est meilleur! Oui c'est bon et simple cas, vérifiez ma réponse.


5 Réponses :


9
votes

en cours d'exécution sur plusieurs threads, avec ce gros tableau une éventail linéaire net sera une vitesse linéaire. C'est gêné parallèlement comme on dit.


8 commentaires

Bonne idée. Essayez de regarder le parallèle pour la boucle dans omnithreadlibrary.


La seule préoccupation serait que vous voulez généralement des morceaux d'au moins 20 km si elles ne sont pas des itérations 50k par tâche dans un parallèle de boucle. À moins que vous ne soyez prudent, ce trivial pourrait être plus lent à moins que vous puissiez déplacer la configuration de la filetage et la démolition des régions critiques de la performance. Essayez-le par tous les moyens, mais je commencerais avec la couper en deux plutôt que dans 20 segments.


@MOZ on ne peut que l'imaginer que ce code fonctionne encore et encore. Si c'était seulement couru une fois ce ne serait pas un problème. Donc, clairement, vous devez faire tourner tous les fils et les faire bloquer lors d'un événement jusqu'à ce que vous ayez besoin d'eux de courir à nouveau - des trucs classiques Threadpool.


Cela augmentera probablement la performance le plus, avec le plus d'effort. Je vais essayer si désactiver la vérification de la plage n'augmente pas suffisamment la performance


@David Heffernan: Nous pouvons imaginer, définitivement. Mais nous ne savons pas. Donc, suggérer une mesure est l'autre partie vraiment précieuse de la discussion. Dresser le omnithreadlibrary dans la suggestion de Mason et la mesure de la modification est probablement la solution la meilleure et la meilleure. Créer une descendante de Ththread et instantiquant un couple de threads pour la tâche pourrait fonctionner, mais ce n'est pas une bonne solution.


@ MMOZ Comment utilisez-vous une piscine de fil sans instanciation de threads?


@David Heffernan: Ce n'est pas si vous instaniez des threads, c'est quand vous le faites. Si vous les tirez à un stade à faible demande de votre demande et que vous les gardez autour du coût, c'est assez non pertinent. Si vous attendez jusqu'à ce que la performance soit essentielle pour les créer, vous pouvez rendre la tâche ci-dessus plus lente.


@MOZ Je préconise faire tout ce qui le rend plus rapide, évidemment!



9
votes

La soustraction en cours d'exécution sur plus de threads sonne bien, mais 100k Integer Sunstraction Ne prenez pas beaucoup de temps CPU, alors peut-être peut-être threadpool ... Toutefois, les threads de réglage ont également beaucoup de frais généraux, de sorte que les tableaux courts auront une productivité plus lente en parallèle threads que dans un seul thread seulement!

Avez-vous éteint dans les paramètres du compilateur, le débordement et la vérification de la plage?

Vous pouvez essayer d'utiliser la rutine ASM, c'est Très simple ...

quelque chose comme: xxx

Il peut être beaucoup plus rapide ...

EDIT: Activité ajoutée avec les instructions SIMD. Cette procédure demande la prise en charge de la CPU SSE. Il peut prendre 4 entiers dans le registre XMM et soustraire à la fois. Il est également possible d'utiliser movdqa au lieu de la page movdqu Il est plus rapide, mais vous devez d'abord assurer l'aliment de 16 octets. Vous pouvez également dérouler le XMM par exemple dans mon premier cas ASM. (Je suis intéressant de la mesure de la vitesse. :)) xxx


8 commentaires

Cela semble très intéressant, je ne suis pas trop familier avec l'ASM. Quel est le type si le paramètre AR1 et AR2 de votre routine?


Type d'AR1 et AR2 est n'importe quel pointeur de variable. Dans notre cas, l'adresse du premier élément du tableau ou de l'array1 [0]. Et oui, le type Array1 (et Array2) peut être une matrice Dinamic ou statique!


Procédure ajoutée avec les instructions SIMD!


Je donnerais un autre +1 si possible.


@Gj. Que diriez-vous de 64 bits? :) Jolie s'il-vous-plaît.


@Gj. Pas vraiment compétent dans ASM . Peut-être que si c'était 3 lignes de ASM . Ceci a simd donc non.


@Gj. Quelles instructions doivent être converties pour permettre de travailler sur 64 bit ?


@ user3764855: Les instructions sont identiques, seuls les registres sont 64 bits. N'oubliez pas que des registres d'entrée également en tant que paramètres de fonction ne sont pas les mêmes.



4
votes

Je ne suis pas expert de l'Assemblée, mais je pense que ce qui suit est proche optimal si vous ne prenez pas en compte les instructions de SIMD ou le traitement parallèle, le plus tard peut être facilement accompli en passant des parties de la matrice à la fonction.

comme
Fil1: Subard (AR1 [0], AR2 [0], 50);
Fil2: Subard (AR1 [50], AR2 [50], 50); xxx


1 commentaires

Juste un peu d'optimisation ... vous n'avez pas besoin CMP ECX, 0 car Dec ECX doit définir le drapeau zéro!



2
votes

Ce n'est pas une vraie réponse à votre question, mais j'éditionnerais si je pouvais effectuer la soustraction déjà à un moment pendant remplissant les tableaux avec des valeurs. Je voudrais même envisager une troisième matrice en mémoire pour stocker le résultat de la soustraction. Dans l'informatique moderne, le «coût» de la mémoire est considérablement inférieur au «coût» du temps qu'il faut pour effectuer une action supplémentaire sur la mémoire.

En théorie, vous obtiendrez au moins une petite performance lorsque la soustraction peut être effectuée tandis que les valeurs sont toujours dans des registres ou du cache de processeur, mais dans la pratique, vous pourriez simplement trébucher sur quelques astuces pouvant améliorer la performance de l'algorithme entier. .


0 commentaires

5
votes

J'étais très curieux de l'optimisation de la vitesse dans ce cas simple. J'ai donc fait 6 procédures simples et mesurer la CPU Tick and Time à la taille de la matrice 100000;

  1. PARTICIPE PASCAL avec la plage d'options de compilateur et le débordement Vérification sur
  2. PROCÉDURE PASCAL avec la plage d'options de compilateur et débordement de débordement
  3. Procédure de Assembleur Classic X86.
  4. Procédure d'assembleur avec instructions SSE et Déplacement de 16 octets non aligné .
  5. Assembleur Procédure avec instructions SSE et Aligné 16 Octet Move .
  6. assembleur 8 fois boucle déroulante avec des instructions SSE et Aligné 16 octets MOVE .

    Vérifiez les résultats sur l'image et le code pour plus d'informations. Entrez la description de l'image ici

    pour obtenir 16 l'alignement de la mémoire de 16 octets Delite Le point dans le fichier 'fastmm4optionS.inc' directive {$ .define align16bytes} ! xxx

    ...

    La procédure ASM la plus rapide avec 8 fois des instructions SIMD déroulées ne prend que 68us et est environ 4 fois plus vite que PROCÉDURE PASCAL.

    Comme nous pouvons voir la procédure de boucle Pascal n'est probablement pas critique, il ne prend que 277us (débordement et gamme de gamme) sur 2,4GHz CPU à 100 000 soustractions. < p> Ce code ne peut donc pas être goulot d'étranglement?


4 commentaires

Je sais que toutes les autres réponses sont correctes, mais j'ai marqué celui-ci parce que tout ensemble


Oh grosse erreur de mon côté. Je viens de remarquer que j'ai déclaré des tableaux d'entier dans mon exemple. Je travaille avec des tableaux de double. Cela fait une grande différence bien sûr


Vous pouvez utiliser SIMD Instruction SUPD (soustraire des valeurs à virgule flottante à double précision emballée). Essayez de faire une nouvelle procédure, puis ouvrez une nouvelle question. :) travaillez-vous avec TCHART


Notez que dans les blocs de taille moyenne FastMMM4 et de grands blocs sont toujours alignés 16 octets, quel que soit le réglage.