Devrais-je réécrire mes routines DSP en C / C ++ ou je suis bon avec les pointeurs non sécurisés C #?

StackOverflow https://stackoverflow.com/questions/261591

Question

Je suis en train d’écrire une application C # qui fait beaucoup de traitement du signal numérique, ce qui implique beaucoup de petites opérations de transfert de mémoire ajustées. J'ai écrit ces routines à l'aide de pointeurs non sécurisés et ils semblent bien mieux performer que je ne le pensais. Cependant, je souhaite que l'application soit aussi rapide que possible.

Aurais-je un avantage en termes de performances en réécrivant ces routines en C ou C ++ ou devrais-je m'en tenir à des pointeurs non sécurisés? J'aimerais savoir ce que les indicateurs non sécuritaires apportent à la table en termes de performances, par rapport à C / C ++.

EDIT: Je ne fais rien de spécial à l’intérieur de ces routines, je me contente de la chose normale du DSP: des transferts de données conviviaux en cache d’un tableau à l’autre avec beaucoup de multiplications, d’additions, de décalages de bits, etc. Je m'attendrais à ce que les routines C / C ++ soient à peu près identiques (sinon identiques) à leurs homologues C #.

EDIT: Merci beaucoup à tous pour vos réponses intelligentes. Ce que j’ai appris, c’est que je n’obtiendrai pas d’augmentation significative des performances simplement en utilisant un port direct, à moins que l’optimisation SSE ne soit réalisée. En supposant que tous les compilateurs modernes en C / C ++ puissent en tirer parti, je suis impatient d'essayer. Si quelqu'un est intéressé par les résultats, faites le moi savoir et je les posterai quelque part. (Peut prendre un peu de temps cependant).

Était-ce utile?

La solution

En fait, j'ai fait à peu près exactement ce que vous demandez, uniquement dans une zone de traitement d'image. J'ai commencé avec des pointeurs non sécurisés C #, puis je suis passé à C ++ / CLI et maintenant je code tout en C ++. Et en fait, à partir de là, je suis passé des indicateurs en C ++ aux instructions du processeur SSE. Je suis donc allé jusqu'au bout. N'ayant pas encore atteint l'assembleur, bien que je ne sache pas si j'en ai besoin, j'ai lu un article sur CodeProject qui montrait que SSE pouvait être aussi rapide que l'assembleur en ligne, je peux le trouver si vous le souhaitez.

Ce qui m'est arrivé au fil des ans, mon algorithme est passé d’environ 1,5 à 2 images par seconde en C # avec des pointeurs non sécurisés, à 40 images par seconde à présent. C # et C ++ / CLI étaient nettement plus lents que C ++, même avec les pointeurs, je n’ai pas pu obtenir plus de 10 images par seconde avec ces langages. Dès que je suis passé au C ++, j'ai obtenu quelque chose comme 15 à 20 images par seconde instantanément. Quelques changements plus astucieux et SSE m'ont permis d'atteindre 40 images par seconde. Alors oui, cela vaut la peine de descendre si vous voulez de la vitesse dans mon expérience. Il y a un net gain de performance.

Autres conseils

Une autre façon d'optimiser le code DSP consiste à le rendre convivial pour le cache. Si vous avez beaucoup de filtres à appliquer à votre signal, vous devez appliquer tous les filtres à chaque point, c’est-à-dire que votre boucle la plus interne doit être sur les filtres et non sur les données, par exemple:

.
for each n do t´[n] = h(g(f(t[n])))

De cette façon, vous réduirez beaucoup moins le cache et obtiendrez probablement une bonne augmentation de vitesse.

Je pense que vous devriez écrire vos routines DSP soit en C ++ (géré ou non), soit en C #, en utilisant une conception solide mais sans essayer d'optimiser tout depuis le début. Vous devez ensuite profiler votre code, rechercher les goulots d'étranglement et essayer. pour optimiser les absences.

Essayer de produire "optimal" Le code dès le départ va vous empêcher de rédiger du code de travail. N'oubliez pas que 80% de votre optimisation n'affectera que 20% de votre code, car dans de nombreux cas, seuls 10% de votre code sont responsables de 90% de votre temps CPU. (YMMV, car cela dépend du type d’application)

Lorsque j'essayais d'optimiser l'utilisation de la fusion alpha dans notre boîte à outils graphiques, j'essayais d'utiliser SIMD, le "métal nu". façon première: assembleur en ligne. Bientôt, j'ai découvert qu'il était préférable d'utiliser les composants intrinsèques SIMD plutôt que l'assemblage pur, car le compilateur est en mesure d'optimiser davantage le C ++ lisible avec les composants intrinsèques en réorganisant les opcodes individuels et en optimisant l'utilisation des différentes unités de traitement de la CPU.

Ne sous-estimez pas la puissance de votre compilateur!

  

Aurais-je des avantages en termes de performances?   de réécrire ces routines en C / C ++   ou devrais-je m'en tenir à des indicateurs dangereux?

En théorie, cela n'aurait pas d'importance - un compilateur parfait optimisera le code, qu'il soit C ou C ++, dans le meilleur assembleur possible.

En pratique, toutefois, C est presque toujours plus rapide, en particulier pour les algorithmes de type pointeur. C’est aussi proche que possible du code machine sans codage en assembleur.

C ++ n’apporte rien à la table en termes de performances - il est construit comme une version orientée objet du C, avec beaucoup plus de capacités et de facilité d’utilisation pour le programmeur. Bien que certaines choses donnent de meilleurs résultats car une application donnée bénéficiera d’un point de vue orienté objet, elle n’a pas été conçue pour être plus performante: elle visait à fournir un autre niveau d’abstraction afin de faciliter la programmation d’applications complexes.

Donc, non, vous ne constaterez probablement pas d’augmentation des performances en passant à C ++.

Cependant, il est probablement plus important pour vous de le découvrir que d’éviter de passer du temps dessus - je pense que ce serait une activité intéressante de le reporter et de l’analyser. Il est tout à fait possible que, si votre processeur dispose de certaines instructions pour l’utilisation de C ++ ou de Java et que le compilateur en ait connaissance, il puisse tirer parti de fonctionnalités non disponibles en C. Peu probable, mais possible.

Cependant, les processeurs DSP sont des bêtes complexes, et plus vous approchez de l’assemblage, meilleures sont les performances que vous obtiendrez (en d’autres termes, plus votre code sera ajusté à la main). C est beaucoup plus proche de l’assemblage que C ++.

-Adam

Permettez-moi d’abord de répondre à la question sur "safe". vs "non sécurisé": vous avez déclaré dans votre message "Je souhaite que l'application soit aussi rapide que possible" et cela signifie que vous ne voulez pas vous mêler de "sûr". ou "géré" pointeurs (ne mentionnez même pas le ramassage des ordures).

En ce qui concerne votre choix de langues: C / C ++ vous permet de travailler avec les données sous-jacentes beaucoup plus facilement, sans la surcharge associée aux conteneurs sophistiqués que tout le monde utilise actuellement. Oui, c’est sympa d’être blotti par des conteneurs qui vous empêchent de commettre des fautes de segmentation ... mais le niveau d’abstraction plus élevé associé aux conteneurs RUINS .

Sur mon travail, notre code doit être rapide. Nos ré-échantillonneurs polyphasés au travail en sont un exemple. Ils fonctionnent avec des pointeurs, des opérations de masquage et un filtrage DSP à point fixe ... aucune de ces astuces intelligentes n’est vraiment possible sans un contrôle de bas niveau de la mémoire et des manipulations de bits == > donc je dis bâton avec C / C ++.

Si vous voulez vraiment être intelligent, écrivez tout votre code DSP au plus bas niveau C. Et mélangez-le ensuite avec les conteneurs / pointeurs gérés plus sûrs ... quand il est trop rapide, vous devez vous décontracter. ils vous ralentissent trop.

(Pour votre information, en ce qui concerne l’enlèvement des roues d’entraînement: vous devez tester votre code DSP C hors ligne pour vous assurer que le pointeur est utilisé correctement ... ou qu’il détecte une anomalie.)

EDIT: p. " défectueux " est un LUXE pour tous les développeurs PC / x86. Lorsque vous écrivez du code intégré ... une erreur de segmentation signifie simplement que votre processeur ira dans les wuides et ne sera récupéré que par un cycle d'alimentation;).

Pour savoir comment obtenir un gain de performances, il est bon de connaître les portions de code qui pourraient causer des goulots d'étranglement.

Puisque vous parlez de petits transferts de mémoire, je suppose que toutes les données vont pouvoir être stockées dans le cache de la CPU. Dans ce cas, le seul avantage que vous puissiez obtenir serait de savoir comment utiliser les éléments intrinsèques du processeur. Généralement, le compilateur le plus familiarisé avec les éléments intrinsèques de la CPU est un compilateur C. Alors ici, je pense que vous pouvez améliorer les performances en effectuant un portage.

Un autre goulot d'étranglement se situe sur le chemin entre le processeur et la mémoire - les erreurs de cache en raison du grand nombre de transferts de mémoire dans votre application. Le principal avantage sera alors de minimiser les erreurs de cache, qui dépendent de la plate-forme que vous utilisez et de la disposition de vos données (sont-elles locales ou réparties dans la mémoire?).

Mais comme vous utilisez déjà des pointeurs non sécurisés, vous avez ce bit sous votre propre contrôle, donc je suppose que: sur cet aspect, vous ne bénéficierez pas beaucoup d'un portage en C (ou C ++).

En conclusion: vous pouvez porter des petites portions de votre application en C.

Étant donné que vous écrivez déjà dans du code non sécurisé, je suppose qu'il serait relativement facile de convertir cela en une dll C et de les appeler à partir de C #. Faites cela après avoir identifié les parties les plus lentes de votre programme, puis remplacez-les par C.

Votre question est en grande partie philosophique. La réponse est la suivante: n'optimisez pas tant que vous n'avez pas votre profil.

Vous demandez si vous gagnerez en amélioration. D'accord, vous gagnerez une amélioration de N pour cent. Si cela suffit (par exemple, vous avez besoin d'un code qui s'exécute 200 fois en 20 millisecondes sur un système embarqué), tout va bien. Mais que se passe-t-il si cela ne suffit pas?

Vous devez d’abord mesurer, puis déterminer si certaines parties du code peuvent être réécrites dans le même langage mais plus rapidement. Peut-être pouvez-vous repenser les structures de données pour éviter les calculs inutiles. Peut-être pouvez-vous éviter certaines réallocations de mémoire. Peut-être que quelque chose est fait avec une complexité quadratique quand cela pourrait être fait avec une complexité linéaire. Vous ne le verrez pas avant de l'avoir mesuré. C’est généralement beaucoup moins de perte de temps que de tout réécrire dans une autre langue.

C # ne prend pas en charge SSE (il existe cependant un projet mono pour les opérations SSE). C / C ++ avec SSE serait donc certainement plus rapide.

Vous devez toutefois faire attention aux transitions gérées vers natives et natives vers gérées, car elles sont assez coûteuses. Restez aussi longtemps que possible dans les deux mondes.

Voulez-vous vraiment que l'application soit aussi rapide que possible ou tout simplement assez rapide? Cela vous indique ce que vous devez faire ensuite.

Si vous insistez pour coller avec votre main-rouleau, sans optimisation manuelle dans assembler ou similaire, le C # devrait être bon. Malheureusement, c’est le genre de question à laquelle on ne peut vraiment répondre que de manière expérimentale. Vous êtes déjà dans un espace de pointeur non géré, mon impression est donc qu'un port direct en C ++ ne verrait pas une différence de vitesse significative.

Je dois dire, cependant, que j’ai eu un problème similaire récemment et que nous avons fini par jeter le rouleau après avoir essayé le Primitives de performances intégrées Intel . Les améliorations de performances que nous avons constatées étaient très impressionnantes.

Mono 2.2 a maintenant le prise en charge de SIMD par ce service, ce qui vous permet d'obtenir le meilleur des deux mondes gérés code et vitesse brute.

Vous pouvez également consulter Utiliser SSE en c #, est-ce possible?

Je suggérerais que si vous avez des algorithmes dans votre code DSP qui doivent être optimisés, vous devriez les écrire en assembleur, et non en C ou C ++.

En général, avec les processeurs et le matériel modernes, peu de scénarios exigent ou justifient les efforts nécessaires à l'optimisation. Avez-vous réellement identifié des problèmes de performances? Sinon, il vaut probablement mieux rester avec ce que vous avez. Il est peu probable que le C # non sécurisé soit significativement plus lent que le C / C ++ dans la plupart des cas d’arithmétique simple.

Avez-vous envisagé C ++ / CLI? Vous pourriez alors avoir le meilleur des deux mondes. Cela vous permettrait même d’utiliser un assembleur en ligne si nécessaire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top