J'ai utilisé le code suivant: int GetAnalogValue (Input) renvoie une valeur de 12 bits à partir d'une distance de mesure du capteur ultrasonique (0-4095, ANALOGVALUERANGE 4096). La double compensation est un membre de la classe d'encapsulation. ReferenceValue est la valeur de référence initiale de referenceInput (normalement, elle a une faible fluctuation, avec une valeur quelque part au milieu de la plage de 12 bits, mais elle change avec la température). RecordData () est appelé toutes les 2 millisecondes (plus ou moins, s'exécutant sous Windows). Donc, un taux de mise à jour élevé pour la valeur de référence, mais je voulais une compensation progressive au fil du temps. Par conséquent, j'ai utilisé une moyenne exponentielle mobile, le delta est déterminé empiriquement et mai besoin d'être modifié plus tard. GetData () est appelé environ toutes les secondes pour vider les données et utiliser l'enregistrement pour une évaluation ultérieure. Nous avons commencé à tester ce code sur les installations du client, mais nous avons obtenu la rétroaction que les valeurs sont à la dérive après quelques jours. Je pense que c'est un algorithme numérique instable qui accumule les erreurs d'arrondi. Est-ce que quelqu'un avec plus d'expérience avec les subtilités de l'arithmétique à virgule flottante me répond: Est-ce que cet algorithme de moyenne mobile exponentielle peut être amélioré pour obtenir une meilleure stabilité? Des notes pour des améliorations supplémentaires du code sont bien accueillies aussi. J'ai profiled cela en utilisant Visual C Profiler, Et il représente environ 35 du temps de fonctionnement. Cette moyenne mobile exponentielle est appelée plus d'un billion de fois, car elle est utilisée à plusieurs reprises sur le traitement de plus de 400 gigaoctets de données. Les données proviennent d'une matrice RAID niveau 0 disque solide, donc la lecture des comptes de données pour moins de 5 du temps. La taille du prix est d'environ 100. J'ai initialement accéléré par un facteur de 4 en précalculant autant de données que possible. Puis j'ai été en mesure de l'augmenter encore par un facteur de ndash PaeneInsula Oct 30 11 at 20:41 J'ai été capable d'augmenter la vitesse de nouveau par un facteur de 12 en multithreading it (la nature des données est telle qu'il peut être multithreaded De sorte que la charge est parfaitement équilibrée.) Et je l'ai en cours d'exécution sur un i7 990x (qui a 6 cœurs, hyperthreaded d'un total de 12), overclocké. Ndash PaeneInsula Oct 30 11 at 20:51 Bien sûr, multithreading peut aider. Mais vous pouvez presque assurément améliorer les performances sur une seule machine filetée. Tout d'abord, vous le calculez dans la mauvaise direction. Seules les machines les plus modernes peuvent effectuer des prélèvements de pas négatifs. Presque tous les machihnes sont plus rapides pour les foulées de l'unité. C'est à dire. Changer la direction de la baie de sorte que vous balayez de bas à haut plutôt que de haut en bas est presque toujours mieux. Ensuite, réécrire un peu - s'il vous plaît permettez-moi de raccourcir les noms des variables pour le rendre plus facile à taper: Par ailleurs, je vais commencer à utiliser des raccourcis p pour le prix et s pour le lissage, pour enregistrer la frappe. Je suis fainéant. Mais il est probablement plus rapide à faire La latence entre avgi et avgi-2 est alors 1 multiplier et un add, plutôt qu'une soustraction et une multiplication entre avgi et avgi-1. C'est à dire. Plus de deux fois plus vite. En général, vous voulez réécrire la récurrence de sorte que avgi est calculé en termes de avgj pour j aussi loin que vous pouvez éventuellement aller, sans remplir la machine, soit des unités d'exécution ou des registres. Vous faites essentiellement plus de multiplications dans l'ensemble, afin d'obtenir moins de chaînes de multiples (et soustrait) sur le chemin critique. Passer de avgi-2 à avgi est facile, vous pouvez probablement faire trois et quatre. Exactement jusqu'à quel point dépend de ce que votre machine est, et combien de registres que vous avez. Et la latence de l'additionneur à virgule flottante et du multiplicateur. Ou, mieux encore, la saveur de combiner l'instruction de multiplication-ajouter que vous avez - toutes les machines modernes les ont. Par exemple. Si le MADD ou MSUB est de 7 cycles de long, vous pouvez faire jusqu'à 6 autres calculs dans son ombre, même si vous avez seulement une unité à virgule flottante. Entièrement pipeline. Etc. Moins si pipelined tous les autres cycle, comme est commun pour la double précision sur les puces plus anciennes et les GPU. Le code d'assemblage doit être un logiciel pipeline afin que les itérations de boucles différentes se chevauchent. Un bon compilateur devrait faire cela pour vous, mais vous pourriez avoir à réécrire le code C pour obtenir les meilleures performances. Soit dit en passant: je ne veux pas suggérer que vous devriez créer un tableau d'avg. Au lieu de cela, vous auriez besoin de deux moyennes si avgi est calculé en termes de avgi-2, et ainsi de suite. Vous pouvez utiliser un tableau de avgi si vous voulez, mais je pense que vous avez seulement besoin d'avoir 2 ou 4 avgs, appelé créativement, avg0 et avg1 (2, 3.), et les faire pivoter. Ce genre d'astuce, divisant un accumulateur ou moyenne en deux ou plus, combinant plusieurs étapes de la récurrence, est commun dans le code de haute performance. Oh, oui: precalculate ss, etc. Si je l'ai fait bien, en précision infinie cela serait identique. (Double vérifiez-moi, s'il vous plaît.) Toutefois, en précision finie FP vos résultats peuvent différer, j'espère que légèrement, en raison de différents arrondi. Si le déroulement est correct et que les réponses sont significativement différentes, vous avez probablement un algorithme numériquement instable. Tu es celle que tu devrais savoir. Remarque: les erreurs d'arrondissement en virgule flottante modifient les bits bas de votre réponse. Les deux à cause de réarranger le code, et en utilisant MADD. Je pense que c'est probablement correct, mais vous devez décider. Note: les calculs pour avgi et avgi-1 sont maintenant indépendants. Ainsi, vous pouvez utiliser un jeu d'instructions SIMD, comme Intel SSE2, qui permet l'opération sur deux valeurs 64 bits dans un registre de 128 bits à la fois. Thatll être bon pour presque 2X, sur une machine qui a suffisamment de ALUs. Si vous avez assez de registres pour réécrire avgi en termes de avgi-4 (et je suis sûr que vous faites sur iA64), alors vous pouvez aller 4X de large, si vous avez accès à une machine comme 256 bit AVX. Sur un GPU. Vous pouvez aller pour les récidives plus profondes, réécriture avgi en termes de avgi-8, et ainsi de suite. Certains GPU ont des instructions qui calculent AXB ou même AXBY comme une seule instruction. Bien que thats plus commun pour 32 bits que pour 64 bits de précision. À un certain point, je commencerais probablement à demander: voulez-vous faire cela sur plusieurs prix à la fois Non seulement cela vous aider avec multithreading, il sera également le faire fonctionner sur un GPU. Et en utilisant SIMD large. Minor Late Addition Je suis un peu embarrassé de ne pas avoir appliqué Horners Rule à des expressions comme un peu plus efficace. Résultats légèrement différents avec arrondissement. Dans ma défense, tout compilateur décent devrait le faire pour vous. Mais la règle Hrners rend la chaîne de dépendance plus profonde en termes de multiplication. Vous pourriez avoir besoin de dérouler et pipeline la boucle quelques fois de plus. Ou vous pouvez faire où vous precalculateI savent que cela est réalisable avec boost comme par: Mais je voudrais vraiment éviter d'utiliser boost. J'ai googlé et n'a pas trouvé d'exemples appropriés ou lisibles. Essentiellement, je veux suivre la moyenne mobile d'un flux continu d'un flux de nombres à virgule flottante en utilisant les plus récents numéros 1000 comme un échantillon de données. Quel est le moyen le plus simple pour atteindre ce que j'ai expérimenté avec l'aide d'un tableau circulaire, moyenne mobile exponentielle et une moyenne mobile plus simple et a constaté que les résultats de la matrice circulaire convenait mieux à mes besoins. Si vos besoins sont simples, vous pouvez simplement essayer d'utiliser une moyenne mobile exponentielle. Autrement dit, vous créez une variable d'accumulateur, et comme votre code regarde chaque échantillon, le code met à jour l'accumulateur avec la nouvelle valeur. Vous choisissez un alpha constant qui se situe entre 0 et 1, et calculez ceci: Il vous suffit de trouver une valeur de alpha où l'effet d'un échantillon donné ne dure que pour environ 1000 échantillons. Hmm, je ne suis pas sûr que ce soit approprié pour vous, maintenant que Ive mis ici. Le problème est que 1000 est une fenêtre assez longue pour une moyenne mobile exponentielle Im pas sûr il ya un alpha qui serait la propagation de la moyenne sur les 1000 derniers chiffres, sans underflow dans le calcul en virgule flottante. Mais si vous voulez une moyenne plus petite, comme 30 nombres ou ainsi, c'est une manière très facile et rapide de le faire. A répondu 12 juin à 4:44 1 sur votre poste. La moyenne mobile exponentielle peut permettre à l'alpha d'être variable. Ainsi, cela permet de calculer des moyennes de base de temps (par exemple, des octets par seconde). Si le temps écoulé depuis la dernière mise à jour de l'accumulateur est supérieur à 1 seconde, laissez alpha be 1.0. Sinon, vous pouvez laisser alpha be (usecs depuis la dernière mise à jour1000000). Ndash jxh 12 juin à 6:21 Je veux essentiellement suivre la moyenne mobile d'un flux continu d'un flux de nombres à virgule flottante en utilisant les plus récents numéros 1000 comme un échantillon de données. Notez que la mise à jour ci-dessous le total en tant qu'éléments comme addedreplaced, en évitant coûteux O (N) traversal pour calculer la somme - nécessaire pour la moyenne - sur demande. Le total est fait d'un paramètre différent de T par rapport au support, par ex. En utilisant un long long pour un total de 1000 s longs, un int pour char s, ou un flottant double au total. C'est un peu vicié en ce que les numsamples pourraient dépasser INTMAX - si vous vous inquiétez vous pourriez employer un unsigned long long. Ou utiliser un membre de données bool supplémentaire pour enregistrer quand le conteneur est rempli tout en cyclant numsamples autour du tableau (mieux renommé quelque chose d'inoffensif comme pos). Répondue 12 juin à 5:19 on suppose que l'opérateur quotvoid (échantillon T) est effectivement opérateur quotvoid (T échantillon) quot. Ndash oPless Jun 8 14 at 11:52 oPless ahhh. Bien repéré. En fait, je voulais qu'il soit vide opérateur () (T échantillon), mais bien sûr, vous pouvez utiliser n'importe quelle note que vous avez aimé. Correction, merci. Ndash Tony D Jun 8 14 à 14:27
No comments:
Post a Comment