DSP-Algorithmen direkt in C oder Assembler schreiben? [geschlossen]

Ich arbeite an einem DSP-Projekt (IIR-Filterung) auf einem digitalen Signalprozessor (BF706) von Analog Devices mit der mitgelieferten Compiler-Suite CrossCore Studio. Es enthält einige Beispiele für einfache DSP-Sachen wie FIR- und IIR-Filter und Bibliotheksfunktionen dafür. Das Prozessorhandbuch beschreibt den Bauanleitungssatz und kommentiert C nicht.

MEINE Frage ergibt sich aus dieser speziellen Anwendung, aber ich dachte, es gibt eine bewährte Methode, der DSP-Entwickler folgen. Also werde ich es allgemein formulieren:

Was ich durch die mit diesem DSP gelieferten Beispiele erkannt habe, ist, dass ich, wenn ich die für DSP-Anwendungen entwickelten Schaltungen verwenden möchte, in Assembly programmieren muss, um diese Anweisungen direkt auszuführen (wie Multiplizieren und Addieren usw.). Meine Frage ist ob Ich programmiere nur in C, würde der Compiler (der auch von der DSP-Chip-Firma stammt) ihn nicht für diesen DSP optimieren und seine Fähigkeiten nutzen? Oder muss ich DSP-Routinen wirklich direkt in Assembly schreiben?

Und was sind die Empfehlungen des Herstellers? Bietet es Entwicklungstools? Anwendungsbeispiele? Codebeispiele? Was Sie wirklich brauchen, ist, sich mehr Mühe in die Recherche der Produktlinie zu stecken, die Sie verwenden werden. Abstimmung zum Schließen als zu weit gefasst.
Ich habe viele Jahre damit verbracht, Assembly für das ADSP-21xx zu schreiben (und später Assembly und C für das Blackfin). Sie geben nicht preis, was Sie verwenden, daher ist jede Antwort eher eine Vermutung und Meinung als alles andere. Aber die DSP-Prozessoren von AD sind verdammt gutes Zeug, und es ist sehr schwer für C-Compiler-Autoren, die Röhre sozusagen richtig zu füllen. Ich habe zwei Jahrzehnte Erfahrung in diesem Bereich (einschließlich einiger sehr bescheidener Erfahrungen beim Schreiben eines C-Compilers) und bis zu dem Zeitpunkt, als ich aufhörte, Code zu schreiben (vor ein paar Jahren), konnten die C-Compiler nicht annähernd an die Handcodierung herankommen. Aber was Sie tun, hängt von Ihren Zielen ab.
@jonk hoffe, dass du eine Antwort darauf schreibst - ich habe immer nur ein Hardcore-DSP-Blackfin-Projekt gemacht, aber ich habe gute Erinnerungen an einige der Performance-Hacks, die es brauchte :)
@pericynthion Nein, ich kann mir nicht vorstellen, eine Antwort darauf zu schreiben, es sei denn, das OP spricht viel mehr über die jeweilige DSP und die Projektziele. Andernfalls wären es vage, ungeleitete Meinungen, die sehr richtig oder sehr falsch sein könnten, je nachdem, was das OP dann darüber geschrieben hat. Also werde ich einfach warten.
Wenn Sie möchten, dass es am schnellsten läuft, optimieren Sie es manuell in der Montage. Das ist ein Kompromiss zwischen Zeit und Geld. Wenn Sie wissen, wie man gutes C schreibt, können Sie das meiste erreichen.
Ich habe Projektdetails hinzugefügt.
Ich habe ein aktuelles Projekt (mit einem ARM M4 + FPU), in dem ich eine begrenzte Menge an DSP (hauptsächlich FIR) mache und der C-Compiler gute Arbeit leistet, aber nicht alle Funktionen der Gleitkommaeinheit nutzt. Allerdings ist die Ausführungsleistung innerhalb der Zeitfenster, auf die ich beschränkt bin, akzeptabel, sodass bei diesem speziellen Auftrag keine Handrollmontage erforderlich ist.
Ich bin mir bei DSP nicht sicher, aber für die meisten Mikroprozessoren können Sie Intrinsics verwenden , die auf halbem Weg zwischen dem Schreiben von Assembler- und C-Code liegen.
Während linux-kongress.org/2009/slides/… (PDF-Link) ziemlich antik geworden ist, lohnt es sich immer noch, es zu überfliegen. Zitat von Money: "Hinweis: gcc ist auf allen Plattformen intelligenter als der Video-Codec-Programmierer." (wobei alle Plattformen in diesem Fall große Desktop-Maschinen bedeuten). Nun, eingebettete Plattformen haben wahrscheinlich nicht ganz die ultraglatte Optimierungsunterstützung, die die wichtigsten Desktop-Plattformen bekommen, aber Sie sollten auf jeden Fall den Compiler ausprobieren.

Antworten (8)

Es ist immer besser, Ihren Algorithmus in einer höheren Sprache (die C mit Assembler vergleicht) zu implementieren, auch wenn Sie vorhaben, am Ende alles in Assembler zu implementieren.

  • Die Chancen stehen gut, dass Sie nicht einmal eine Montage benötigen . Wenn der von Ihrem Compiler generierte Code Ihren Designzielen entspricht, ist Ihre Arbeit erledigt.

  • Wenn nicht, werden Sie Ihre Assembly-Codierung nicht von Grund auf neu beginnen . Lassen Sie den Compiler den anfänglichen Code für Sie generieren und verwenden Sie diesen als Basis für Ihre optimierte Assembly-Version.

  • später, wenn Sie Ihren optimierten Assembler-Code testen müssen , werden Sie froh sein, die C-Version zu haben. Anstatt die korrekte Ausgabe für Ihre Testeingabedaten manuell zu berechnen, können Sie diese Eingabedaten einfach in Ihre nicht optimierte C-Implementierung einspeisen und dann überprüfen, ob die Assembly nach den von Ihnen vorgenommenen Optimierungen genau die gleiche Ausgabe erzeugt.

Wenn ein neuer Entwickler nach ein paar Jahren Änderungen an Ihrem Algorithmus vornehmen muss und nur einen hochoptimierten Assembler-Code zur Hand hat, besteht eine hohe Wahrscheinlichkeit, dass er bei Null anfangen muss.

Wenn die Compiler-Autoren sich Mühe geben, ihn für dieses Ziel zu optimieren, wird er zumindest die speziellen DSP-Anweisungen / -Architekturen nutzen. Aber für die ultimative Leistung wird es nie so gut sein wie eine von Hand abgestimmte Montage. Es könnte jedoch gut genug sein - hängt von Ihrer Anwendung ab.

Andere Alternativen sind:

  1. Schreiben Sie den Großteil Ihres Programms in C und nur den kritischsten numerischen Teil in Assembler.
  2. Schreiben Sie das Programm in C und verwenden Sie Bibliotheken, die vom Hersteller oder von Drittanbietern bereitgestellt werden. Wenn Sie allgemeine DSP-Aufgaben wie FFTs, FIR / IIR-Filter usw. ausführen, hat wahrscheinlich bereits jemand den handabgestimmten Maschinencode dafür geschrieben, also Sie können Sie diese verwenden (möglicherweise müssen Sie dafür bezahlen) und sie mit Ihrer Anwendung verknüpfen.
Normalerweise liefern die DSP-Anbieter den Quellcode für die gemeinsamen Funktionen. Wenn ihr Code "gut genug" ist, können Sie ihn direkt einfügen. Wenn er nicht ganz richtig ist, müssen Sie ihn optimieren. Ich musste vor einigen Jahren eine FFT-Schicht erstellen, um eine reine Frequenz-FFT zu erhalten. Es gibt einen Trick, mit dem Sie eine echte 2N-Punkt-FFT als komplexe N-Punkt-FFT ausführen können, aber dann müssen Sie einen letzten Durchgang über den komplexen Ausgang ausführen, um die echten Frequenzdaten wiederherzustellen. Analog Devices hatte diesen speziellen Fall nicht in ihrem Beispielcode.

Vorzeitige Optimierung ist die Wurzel allen Übels. -Donald Knuth

Wenn Sie feststellen, dass Ihr Code nicht genügend Leistung bringt, erstellen Sie zuerst ein Profil Ihres Programms, finden Sie die Engpässe, analysieren Sie Ihre Leistungsanforderungen und beginnen Sie erst dann mit der Optimierung. Das Schreiben von Assemblercode ist der letzte Ausweg.

Meine Frage ist, wenn ich nur in C programmiere, würde der Compiler (der auch von der DSP-Chip-Firma stammt) ihn nicht für diesen DSP optimieren und seine Fähigkeiten nutzen?

Ja, der C-Compiler kann ziemlich viel optimieren. Dies hängt jedoch von der Qualität des Compilers ab. Häufig kann ein Mensch schnelleren Assemblercode schreiben als den kompilierten C-Code. Das heißt, auf Kosten menschlichen Schmerzes und Leidens.

Oder muss ich DSP-Routinen wirklich direkt in Assembly schreiben?

Schreiben Sie zuerst in C, dann im Profil und entscheiden Sie dann, ob Sie in Assembly schreiben müssen. Hoffentlich würden Sie die Versammlung nicht brauchen.

In der allgemeinen Programmierung ist dies sicherlich ein guter Rat, aber DSP ist etwas anders - wenn das OP einen DSP wirklich effizient nutzen möchte, muss wahrscheinlich irgendwo auf der Linie handgeschriebener Code vorhanden sein. Und tatsächlich möchte man bei DSP-Projekten manchmal sogar damit beginnen, den numerischen Kernel zu schreiben, um zu validieren, dass der Prozessor für die anstehende Aufgabe geeignet ist.
Ihre abschließende Aussage ist ein guter allgemeiner Rat. Aber es ist etwas blass, wenn man die spezifischen Details der AD-DSP-ALUs betrachtet. Ich nehme nicht an, dass Sie sie jemals untersucht haben.

Ihr DSP wird mit maximal anhaltenden MACs beworben, vorausgesetzt, alle Pipes sind gefüllt. Das ist natürlich eine Obergrenze dessen, was erreicht werden kann. Aus Ihrer Analyse wissen Sie, wie viele MACs Ihre Filter und andere Verarbeitungen benötigen. Versuchen Sie, den ersten mindestens doppelt so hoch wie den zweiten zu haben, da Sie den DSP-Kern nicht auf Maximum laufen lassen können. Genauso wie Sie nicht versuchen würden, ein FPGA mit mehr als 70 % Ressourcen zu füllen (PAR wird darüber sehr langsam), könnte die Entwicklung sehr langsam werden, wenn Sie versuchen, die letzten paar theoretischen MACs aus einem DSP herauszuquetschen.

Sie werden Ihre gesamte Anwendung in C codieren. Es ist unpraktisch, all die zusätzlichen, notwendigen Dinge in Assembler zu schreiben, Testinjektion und Sichtbarkeit, Haushalt usw. Schreiben Sie eine C-Version des Testfilters. Schreiben Sie eine Assembler-Version desselben Filters, um zu überprüfen, ob Sie tatsächlich Assembler für dieses Biest schreiben können.

Machen Sie jetzt einige Zeitmessungen. Verwenden Sie ein vom Lieferanten genehmigtes RTOS. Vergleichen Sie die Laufzeit Ihres Test-Assembler-Moduls mit einer C-Version. Wenn sie innerhalb weniger Prozent liegen, fahren Sie fort. Wenn es dreifach ist, lesen Sie die Dokumentation, fragen Sie den Anbieter ab und finden Sie heraus, warum der Compiler es nicht optimiert. Möglicherweise müssen Sie lernen, seine Variante von C zu schreiben und die richtigen Compiler-Flags zu setzen. Es wird schneller sein, herauszufinden, wie der Compiler richtig gesteuert wird, als alles in Assembler neu zu schreiben.

Sie haben all dies getan, bevor Sie sich auf einen DSP, auf eine Werkzeugkette festgelegt haben.

Sobald Sie eine Toolchain haben, mit der Sie arbeiten können, einen Compiler, den Sie so einstellen können, dass er einigermaßen nahe an das Maximum herankommt, einen DSP mit etwas verbleibendem Timing-Spielraum, dann können Sie einigermaßen sicher sein, dass nur sehr wenige Teile Ihrer Code-Suite eingefügt werden müssen Monteur, um die Arbeit zu beenden.

Obwohl ich diese Frage bereits beantwortet habe, werde ich eine weitere Antwort hinzufügen, um einen anderen Standpunkt zu veranschaulichen:

In C schreiben, in Assembler lesen!

Anstatt also in Assembler zu schreiben, schreiben Sie die Logik in C und stellen sorgfältig sicher, dass die Assembler-Ausgabe des C-Codes optimal ist. Sie können oft bestimmte Tricks im C-Code anwenden, um die Assembler-Ausgabe zu beeinflussen. Verwenden Sie statische Inline-Funktionen, wenn es sinnvoll ist. Wenn Sie einige spezielle Anweisungen verwenden müssen, die der DSP unterstützt, erstellen Sie eine statische Inline-Funktionsabstraktion der speziellen Anweisung und rufen Sie die spezielle Anweisung unter Verwendung der Abstraktion auf.

Obwohl ich sagen muss, dass ich noch nie DSPs programmiert habe, hat dieser Ansatz, den C-Code zu schreiben und dabei die kompilierte Assembly genau zu beobachten, für mich auf x86-Rechnern sehr gut funktioniert. So gut, dass ich nie etwas in Assembler schreiben musste, um die bestmögliche Leistung zu erzielen. Anstatt den Assemblercode zu optimieren, werde ich den C-Code so modifizieren, dass die Assemblierung optimal ist.

Dies hängt natürlich davon ab, ob gute C-Compiler verfügbar sind. Für x86 sind solche Compiler verfügbar (man muss oft eine höhere Optimierungsstufe als die Standardeinstellung angeben). Für DSPs weiß ich ehrlich gesagt nicht, ob die Compiler so gut sind.

Der Vorteil dieses Ansatzes besteht darin, dass Sie eine einzige tragbare Codebasis haben, die optimiert ist, um zu einer optimalen Zusammenstellung für einen bestimmten DSP zu führen, aber es funktioniert auch, wenn der DSP auf etwas anderes geändert wird. Natürlich müssen Sie den C-Code möglicherweise leicht anpassen, um die bestmögliche Leistung auf dem neuen DSP zu erzielen.

Ich habe eine Frage dazu: Ich arbeite mit STM32F4 Cortex-M4-Prozessoren und verwende die CMSIS/Cube-Bibliotheken. Ich verwende auch das -O3-Flag des Compilers, weil es sich als effizienter erwiesen hat als alles, was ich produzieren könnte. Das Problem ist, dass die kompilierte Assembly für eine ordnungsgemäße Analyse immer viel zu chaotisch ist. Kompilieren Sie immer ohne Compiler-Optimierung? Oder schaffen Sie es, den Versammlungsabend zu verstehen, wenn es überall so ist?
@FlorentEcochard: Wenn der Assembler des Compilers von einem Programmierer nicht verstanden werden kann, ist er wahrscheinlich besser als der Assembler, den dieser Programmierer schreiben kann. Als direkte Antwort auf Ihre Frage: Verwenden Sie maximale Optimierung und manuelle Analyse des Monteurs, schwierige Teile könnten erzieherisch sein.

Im Allgemeinen ist es nicht notwendig, Assemblerquellen zu schreiben, wenn:

  • Sie optimieren C in den kritischen Abschnitten: eine gute Verwendung des Schlüsselworts "register", Inline-Funktionen, ...
  • könnten einige Funktionen des C-Programms sein, die asm- Blöcke verwenden

Das bedeutet , den vom C-Compiler generierten Assembler (auf die kritischen Teile) manuell zu überprüfen und den Quellcode zu ändern, bis ein ausreichendes Optimierungsniveau erreicht ist.

Praktisch alle modernen Compiler ignorieren das Schlüsselwort "register", unabhängig von der Plattform. Es ist sehr unwahrscheinlich, dass seine Verwendung zu besserem Code führt.
@KefSchecter: Sie berücksichtigen nicht nur den Registerhinweis, heutzutage können sie sogar das zu verwendende Register auswählen: gcc.gnu.org/onlinedocs/gcc-6.1.0/gcc/…
@KefSchecter: mit Ausnahme von Compilern, die für eingebettete Geräte geschrieben wurden, wo es ein sehr wichtiges Schlüsselwort ist, wenn Sie auf Bare Metal programmieren.
@pasabaporaqui: Ich habe diesen Syntax vergessen. Aber wenn Sie keinen Registernamen angeben – mit anderen Worten, wenn Sie ihn nach ISO-Standard verwenden – wette ich, dass GCC ihn ignorieren wird.

Ich würde hier sagen, dass es bei FIR / IIR-Filtern weitaus wichtiger ist, welchen Algorithmus Sie verwenden (den trivialen Algorithmus oder die schnelle Fourier-Transformation (FFT)) als welche Sprache Sie verwenden (C oder Assembly).

Würde ich FFT in Assembler schreiben? Wahrscheinlich nicht.

Würde ich selbst FFT schreiben? Die Antwort darauf ist wahrscheinlich auch nicht, da FFT bereits viele Male implementiert wurde. Die Chancen stehen gut, dass Sie eine Bibliothek finden, die FFT bereits implementiert hat. Wenn man bedenkt, dass C eine portable Sprache ist, Assembler jedoch nicht, werden Sie viel wahrscheinlicher vorhandene Bibliotheken finden, die bereits in C implementiert sind.

Wenn Sie die höchstmögliche Leistung wünschen, können Sie natürlich einen FFT-Algorithmus manuell so einstellen, dass er so schnell wie möglich in Assemblersprache arbeitet. Aber ich glaube nicht wirklich, dass es Sinn macht, das zu tun, außer in sehr außergewöhnlichen Umständen.

Meine eigene Ansicht FWIW ist, dass Assembler immer dann Ihr Freund ist, wenn Sie maximale Geschwindigkeit / Effizienz / Durchsatz / was auch immer wollen, solange Sie sich auskennen. Ein Compiler ist dumm; es "weiß" nur, was sein Autor zu programmieren gedacht hat, und sein Autor kannte Ihre Anwendung überhaupt nicht.

Ich muss zugeben, dass ich seit Anfang der 80er Jahre 8-Bit-Mikros in Assembler liebe (in vielerlei Hinsicht nicht unähnlich zu modernen MCUs), bei denen das Erlernen von "Maschinencode" eine Voraussetzung war, um eine nützliche Leistung aus ihnen herauszuholen, aber ich denke, seine Rolle bleibt bestehen als Weg zur Programmierung für maximale Effizienz. Außerdem ist es sehr lohnend, da Sie alle Arten von Optimierungskürzeln einwerfen können, an die ein Compiler nicht denken wird, weil ein Compiler überhaupt nicht denken kann.

C ist okay denke ich. Aber wenn Sie wirklich wissen, was Ihre Maschine auf Hardwareebene tun soll, gehen Sie zum Assembler.