Muss manches in Assembler geschrieben werden?



  • Ja teilweise musst du auf Assembler zugreifen weil C oder andere Hochsprachen teilweise einfach zu wenig Kontrolle über die Hardware zulassen.
    Z.B. bei Betriebsystemen, Platformen (z.B. Mikrocontroller) für die kein Compiler existiert, VMs, oder einfach brandneuen Features im Instruktionset die der Compiler noch nicht kennt.

    Ich glaube man hätte den SPC700 auch anderes emulieren können, zumindest große Teile hätte man in C schreiben können. Vielleicht wäre es in C aber umständlich geworden während man es in Assembler kurz schreibe kann; oder man hat gedacht dass die Performance so besser ist.

    Manchmal noch, wenn das Programm in Echtzeit laufen soll und garantiert werden muss, dass ein gewisser Codeteil nicht länger als X Ticks läuft

    Ich dachte das geht bei moderne CPUs mit ihrem Out-Of-Order_Execution, parallenen Pipelines und Caches sowieso schwierig ?



  • DarkShadow44 schrieb:

    Manchmal noch, wenn das Programm in Echtzeit laufen soll und garantiert werden muss, dass ein gewisser Codeteil nicht länger als X Ticks läuft

    Ich dachte das geht bei moderne CPUs mit ihrem Out-Of-Order_Execution, parallenen Pipelines und Caches sowieso schwierig ?

    Für harte Echtzeitanforderungen beachtet man in der Regel nur den worst case. Entsprechend wird eh davon ausgegangen das die benötigten Daten nicht im Cache stehen und Operationen nicht parallelisiert werden können.

    An dem Lehrstuhl bei dem ich meine Diplomarbeit geschrieben habe, wurden sogar Compiler entwickelt, die sich nur um die WCET gekümmert haben und Caches etc. ignoriert haben. Die Idee dahinter war, dass man seine Laufzeitschranken angibt und am Ende die minimale Taktrate des benötigten Prozessors bekommt um das günstigste Modell wählen zu können.



  • Assembler ist nach wie vor ein muss wenn es wirklich um Geschwindigkeit geht. Kodieren, Dekodieren, Mathematische Bibliotheken etc. Ein Compiler kann bis heute nicht so gut optimieren wie ein Mensch, denn jedes Feature einer CPU kann ein Compiler für das aktuelle Problem optimal umsetzen.

    Früher habe die Kids nur Assembler programmiert(Ich hatte da mit 13 angefangen 3D Sachen zu machen), da nur so die wirklich schwache Hardware zu was tollem zu gebrauchen war. Heute ist alles schnell genug und man muss als Normalsterblicher nie einen Assembler gesehen haben. Aber wie gesagt wenn du wirklich schneller sein willst beim Packen, Verschlüsseln etc. dann führt kein Weg dran vorbei, die Sprache zu sprechen die die CPU versteht. Der Dolmetscher muss dann halt mal weglassen werden, denn eine CPU kennt kein C oder C++. Früher waren C Programme im Vergleich zu Assembler elend lahm und heute ist es zwar besser aber im Prinzip auch noch so, nur sind die Maschine um ein so vieles schneller und der Speicher viel größer, das es nicht mehr auffällt. 1 bis 8Mhz und 64k bis 512k RAM waren alles was man zu meiner Zeit hatte und da wurden dann noch was fürs Betriebssystem abgegriffen.



  • Assembler4Speed schrieb:

    Assembler ist nach wie vor ein muss wenn es wirklich um Geschwindigkeit geht.

    Ach, Unfug. Ganz oft gewinnt der Hochsprachencode, weil man sich damit an schnellere Datenstrukturen traut, mehr Überblick hat und einem algorithmische Optimierungen einfallen, weil man mehr Zeit hat zu recherchieren und so.

    Bei beliebig viel Entwicklungszeit hast in machen Beispielen recht. Aber den Compiler zu schlagen, da muss es schon um neue Prozessorfeatures gehen oder was, wo er einfach blind ist.



  • Vielen Dank an euch für die Antworten. Ist ja interessant. Ich dachte bis jetzt, dass man alles in einer Hochsprache schreiben könnte. Ich habe mal etwas über die Entwicklung auf Basis des SNES gelesen und da wurden die spiele tatsächlich sehr oft in Assembler geschrieben. Allerdings gab es auch einen C Compiler für den SNES, den man sich heute immer noch herunterladen kann.

    Bei dem Code, den ich oben verlinkt habe, spricht man ja allerdings nicht wirklich die Hardware des SNES an, sondern versucht diese zu emulieren. Oder benötigt die Emulation von Hardware so viel Rechenleistung?

    Übrigens meine ich gelesen zu haben, dass der obige Code keine speziellen Features einer bestimmten CPU nutzt, sondern für den Betrieb nur vorraussetzt, dass die CPU mit 32 BIT Software umgehen kann.

    Die dll hab ich nämlich schon auf mehreren Computern im Winamp verwendet, was glaub ich ja nicht ginge, wenn der Code bestimmte CPU Features nutzen würde, oder?



  • da wurden die spiele tatsächlich sehr oft in Assembler geschrieben

    Naja der SNES ist ja schon älter, mittlerweile sind die Compiler einfach deutlich besser geworden 😉

    Und ja, Emulation braucht extrem viel Rechenleistung. Aber wenn du guten C-Code schreibst bekommst du da genug Performance. Effizienten Code zu schreiben ist das größere Problem als der Asm-Code des Compilers.

    Übrigens meine ich gelesen zu haben, dass der obige Code keine speziellen Features einer bestimmten CPU nutzt, sondern für den Betrieb nur vorraussetzt, dass die CPU mit 32 BIT Software umgehen kann.

    Das kommt darauf an ob und welche Erweiterungen der x86 Architektur der Code verwendet, vielleicht läuft er auf sehr alten Rechnern nicht. Aber besonders bei sowas hast du mit einen Hochsprachenprogramm Vorteile. Wenn du neue Features (z.B. SSE/MMX/AVX) verwenden willst musst du ein Assemblerprogram komplett umschreiben. Ansonten beim Compiler einfach die Einstellung ändern und neukompilieren.



  • Kein Compiler kann wirklich perfekten SSE/MMX Code produzieren, da ist auch heute noch Handarbeit angesagt und ja, dann hat man halt für jede CPU-Generation einen eigene Decodierroutine, immer noch besser als neue Features einer CPU brach liegen zu lassen.



  • Kein Compiler kann wirklich perfekten SSE/MMX Code produzieren,

    Das behauptet auch keiner...

    da ist auch heute noch Handarbeit angesagt

    Ja was heißt denn überhaupt perfekten Code? Auslastung CPU maximal, Anzahl Cache Misses minimal, Anzahl der Zugriffe auf Speicher, Festplatte minimal, Durchsatz maximal, optimale Branch Prediction.

    Perfekten Code ist ein Optimierungsproblem und vom Menschen nicht mehr im Kopf zu bestimmen.

    Ergebe dich nicht dem Schein dass ein Befehl wirklich schneller ist, nur weil er X Takte weniger benötigt. Es mag sein dass der Befehl deinen Durchsatz vermasselt, weil diese keine optimale Branch Prediction hat.



  • volkard schrieb:

    Assembler4Speed schrieb:

    Assembler ist nach wie vor ein muss wenn es wirklich um Geschwindigkeit geht.

    Ach, Unfug. Ganz oft gewinnt der Hochsprachencode, weil man sich damit an schnellere Datenstrukturen traut, mehr Überblick hat und einem algorithmische Optimierungen einfallen, weil man mehr Zeit hat zu recherchieren und so.

    Bei beliebig viel Entwicklungszeit hast in machen Beispielen recht. Aber den Compiler zu schlagen, da muss es schon um neue Prozessorfeatures gehen oder was, wo er einfach blind ist.

    genau das meint er wohl mit "wenn es wirklich um Geschwindigkeit geht". z.B. video codec, math libraries von cpu/gpu firmen, ....

    Die von dir genannten optimierungen sind dabei nur der Anfang, es ist ja gut und billig sich vom compiler die erste version vom assembler generieren zu lassen und danach optimiert man auf unterer ebene.
    und natuerlich ist jede weitere optimierung die man high-level machen moechte, gerade wenn man datenstrukturen aendert, sehr zeit intensiv, aber kompiler sind nunmal blind.
    1. kennt der compiler nicht das anwendungsgebiet, wenn du z.b.
    - einen if-check machst, weiss der compiler zwar wie er den machen muss damit die cpu-jump-prediction macht was der compiler will, aber der compiler weiss nicht mit welcher intention du diesen if-check gesetzt hast, er trifft eine annahme. wenn du z.b. schreibst

    if(foo)
      StartSomething();
    .
    .
    .
    if(foo)
      StopSomething();
    

    kann es durchaus sein, dass der compiler es zu

    if(foo)
    {
      StartSomething();
    .
    .
    .
      StopSomething();
    }
    else
    {
    .
    .
    .
    }
    

    optimiert (hat mir der gcc fuer cell-spu gemacht), was legitim ist, aber du hast eine harte zeit den compiler dann wieder vom gegenteil zu ueberzeugen, ohne dass du die anderen optimierungen die er macht zunichte machst.
    - wenn du eine funktion aufrufst, weiss der compiler nicht, ob inlinen um den sprung zu ersparen effizienter ist, oder nicht inlinen um cache freundlicher zu sein, es kann sein dass du die zu inlinende funktion um ein paar zeilen source erweiterst und diese dann anders bewertet wird und nicht mehr geinlined wird. (unter der premisse dass man normalen code schreibt und nicht von vornhinein alles mit attributes fuer force-inline und no-inline versieht, was ja low level waere).

    2. hat der compiler kein wissen ueber die daten die verwendet werden. aber wie du schon sagtest, dass das richtige layout der daten wichtig ist, ist auch der inhalt der daten wichtig. zu wissen in welchen gruppen die daten kommen, wie weit daten gestreut sind auf die zugegriffen wird kann leicht einen grossen unterschied machen. wenn du sowas schreibst wie

    for(int i=0;i<Count;i++)
      Out[a]=process(In[a]);
    

    kann es sein dass die daten immer
    - aligned sind oder nicht
    - immer ein mehrfaches von z.B. 4 sind oder nicht
    - immer, obwohl "Count" ein hartes limit hat, noch darueber hinaus verwendet werden koennen, dann lediglich irrelevant sind.
    - die datenmengen riesig oder winzig sind
    3. generiert der compiler immer anderen code (jedenfalls darfst du nicht fest davon ausgehen, was er macht, denn ein compiler muss nur legitimen code, also code der richtige resultate liefert erzeugen, wie er das macht, das ist in keinem standard definiert). mir hat z.b. ein compiler aus

    void food()
    {
      struct Bar{float x,y,z;}
      Bar Stack[...];
      int StackPt[...];
    }
    

    es nach einem compiler update umgebaut zu

    struct Bar{float x,y,z;}
      {Bar,int} Stack[...];//ich hoffe das ist verstaendlich
    

    was an sich auch effizienter ausschaut, weil man aligned auf die daten zugreifen kann, aber ich hatte auf Stack im gegensatz zu StackPt mit 2x indiziert. was im endeffekt dafuer sorgte dass dieser wichtige teil 10% langsammer lief. (glaube das war ein upgrade von CUDA 3.2 auf CUDA 4.0).
    4. compiler arbeiten nur innerhalb eines kleinen scopes und muessen das nehmen was du ihnen gibst, so wie du es gibst. deswegen gibt es auch optimization guides der einzelnen cpu hersteller. z.B. sagt AMDs paper man sollte daten eher so kopieren

    for(size_t i=0;i<size;i++)
     out[i]=in[i];
    

    ARM hingegen sagt, dass man das eher so machen sollte:

    while(pOut<pEnd)
     *pOut++=*pIn++;
    

    natuerlich kann man das im weiteren context sehen (also nicht nur wenn es ums kopieren geht, denn die code generierung dafuer sollte mittlerweile jeder compiler ans optimum gebracht haben). beide versionen damals mit gcc fuer ARM compiliert und zweiteres war schneller. wobei ein legitimer compiler zweiteres nicht zum ersteren umbasteln kann wegen aliasing (schliesslich koennte pOut auf sich selbst zeigen).

    das traurige ist, es ist nicht so wie viele denken, dass man irgendwas kleines in assembler schreibt, denn da wuerde ich dir recht geben, oft kann der compiler das sehr gut, gerade wenn es um register und op-code scheduling geht. oft ist aber alles drumherum was wichtig ist, damit dann dieser kleine code bereich dann wirklich schnell laeuft und das ist echt arbeit, aber kann in 2x-20x performance enden. compiler optimieren in diesem grossen scope nichtmal, denn am ende ist es wie beim menschen, ein completer code rewrite.

    ein paar praktische einblicke von low level optimierungen bekommen man in vielen eintraegen vom x264 blog: http://x264dev.multimedia.cx/
    und ja, auch die sehen dass assembler viel aufwand ist, gerade bei einem tool das in production ist, weil man den optimalen code fuer zig permutationen von platformen erstellen muss (was ein hochsprachen compiler meist garnicht beruecksichtigt). so haben die nicht nur ihren codec mit assembler optimiert, sondern auch einen 'assembler abstraction layer' erstellt: http://x264dev.multimedia.cx/archives/191

    ich glaube die meisten die zu assembler rufen 'ach, bullshit' haben nicht viel damit gearbeitet. man kann natuerlich annehmen dass Scheisse schlecht schmeckt ohne sie zu kosten, aber man kann auch alles was man nie gekostet hat grundsaetzlich als ekelig betiteln, obwohl andere menschen es essen). aber ich lasse mich gerne von gegenbeispielen ueberzeugen.

    (aber es ist auch war, die meisten die eine sprache waehlen wegen geschwindigkeit z.b. js->java, java->c++,c++->assembler, machen erstmal einen schritt zurueck bis sie mit der sprache umgehen lernen und dann noch ewig bis die investierte zeit mehr bringt als wenn man einfach in der bestehenden sprache optimiert haette).

    (sorry fuer soviel text, sollte auch kein basching sein, aber eine detailiertere, differenzierterere antwort sein als einfach auf das 'bullshit' mit 'selber' zu antworten.



  • rapso schrieb:

    (sorry fuer soviel text, sollte auch kein basching sein, aber eine detailiertere, differenzierterere antwort sein als einfach auf das 'bullshit' mit 'selber' zu antworten.

    Viele Gründe sind auch korrekt, aber es gibt auch Tricks, den Compiler auf den richtigen Pfad zu weisen.

    1. kennt der compiler nicht das anwendungsgebiet
    [...]
    2. hat der compiler kein wissen ueber die daten die verwendet werden

    Dafür gibt es PGO, das sollte dein Beispiel erschlagen.
    Für hartnäckige Fälle gibt es builtin_expect und __restrict__.

    Ist zwar compilerspezifisch, aber immerhin flexibler als Assembler.

    3. generiert der compiler immer anderen code

    Kann ich kaum glauben, es ist garantiert, dass stackPt[...] kompakt im Speicher liegt. Aber selbst wenn das nicht der Fall ist, könnte man den Compiler mit std::pair<int,int>stackPt[...] zurechtweisen.

    4. compiler arbeiten nur innerhalb eines kleinen scopes und muessen das nehmen was du ihnen gibst, so wie du es gibst.

    Genau deswegen gibt es -march und -mtune, richtig gesetzt optimiert der Compiler für die gewünschte Plattform. Bestimmte SSE-Befehle können mit builtins abgerufen werden.

    Meiner Erfahrung nach gab es noch nie einen Fall, in dem es nicht möglich war, den Compiler zu zwingen, den gewünschten Assemblercode zu generieren.



  • - wenn du eine funktion aufrufst, weiss der compiler nicht, ob inlinen um den sprung zu ersparen effizienter ist, oder nicht inlinen um cache freundlicher zu sein, es kann sein dass du die zu inlinende funktion um ein paar zeilen source erweiterst und diese dann anders bewertet wird und nicht mehr geinlined wird. (unter der premisse dass man normalen code schreibt und nicht von vornhinein alles mit attributes fuer force-inline und no-inline versieht, was ja low level waere).

    Soweit wie ich weis macht mein Compiler (VS) für jede inline Funktion eine Kosten zu Nutzen Rechnung. Erscheint dem Compiler das Inlinen als nicht effizient, wird die Funktion nicht geinlined.

    Des weiteren existiert eine Compilerschalter für automatisches Inlining.



  • Sehr interessant, was ihr da so schreibt. Die Optimierungsfunktionen meines Compilers sollte ich mir glaub ich auch mal genauer anschauen.

    Geht die Umwandlung von Code eigentlich auch anders herum? Also nicht von einer Hochsprache in Assembler, sondern von Assembler in eine Hochsprache?
    Wenn das ginge, könnte man ja theoretisch den Assembler Code der SPC700 Emulation in eine Hochsprache übersetzen und dann weiterverarbeiten.



  • Das geht zwar, aber was raus kommt ist halt Maschinengenerierter Code. Variablennamen, Funktionsaufteilung etc. können nicht notwendigerweise (meistens mehr oder weniger gar nicht) wiederhergestellt werden. Kommentare natürlich erst recht nicht.

    Edit: Moment mal, du meinst ja Assembler und nicht Binärkram. Das sieht allerdings ähnlich dünn aus, wobei einige Dinge besser zu machen sind. z.B. Kann man versuchen aus Sprunglabels und definierten Konstanten Namen für Variablen abzuleiten, und Kommentare kann man natürlich auch mit nehmen. Schön wird's aber trotzdem nicht.



  • Hmmm... Das ist schade. Also wohl eher eine Notlösung, als ein Sinnvoller Ansatz. Kommt wahrscheinlich auch noch auf den Umfang des Assembler codes an.
    Ich schau mal, ob ich seine Mail Adresse finde. Dann frag ich ihn einfach mal, warum er das in Assembler und nicht in eine Sprache wie C oder C++ geschrieben hat. Denn ich meine, jemand der eine Hardware Emulation in Assembler schreiben kann, muss ja schon irgendwie Ahnung von dem Thema haben und hat vielleicht einen bestimmten Grund, warum er das so und nicht anders gemacht hat.


  • Mod

    Das mag sich vielleicht verwegen anhören, aber: Vielleicht mag er einfach Assembler? Es ist ja anscheinend ein Hobbyprojekt, wo er keine Deadline hat und das Problem so angehen kann, wie es im Spaß macht. Und manche Leute haben tatsächlich Spaß an Assembler.



  • Jupp, klingt gar nicht mal so unlogisch und auch irgendwie nachvollziehbar, da ich das Thema selber spannend finde.
    Leider nur unpraktisch für diejenigen, die das Ding verwenden und warten möchten.



  • Wenn man sich die Zeit der Entstehung anschaut, und das Zielsystem, dann erklärt das schon einiges. DSP-Emulation u.ä. brauchte damals schon eine gewisse Performance (z.B. wie die FPUemulationen noch früher), DOS war extrem assemblerfreundlich und wenn man die Hardware sowieso in und auswendig kennt, dann liegt eine hardwarenahe Emulation irgendwo näher, man hat u.a. mehr Freiheiten auf technischer Ebene, ganz abgesehen von der Tatsache, das Emulatorfreunde ganz oft elektrotechnischen oder hexkodierten Hintergrund haben. Heute ist der Hintergrund ein ganz anderer, darum gibt es z.B. auch so viele lustige in Java programmierte Emulatoren.

    Man könnte genausogut zurückfragen, warum wird heutzutage so viel und mainstreamig in Hochsprachen geschrieben (und warum in C (wie chaotisch) und nicht Pascal)?
    Die Intelplattform ist seit Jahren ziemlich die gleiche und die neueren Assembler sind recht trickreich und leistungsstark und die Assemblerprogrammierung ist ja nicht schwerer als Hochsprachenprogrammierung, sondern eher einfacher, wenn man mal von riesigen Bibliotheken, lizensierten Dokumentationen, Codierhilfsbücher usw. absieht.

    Zurückübersetzen:
    http://de.wikipedia.org/wiki/Decompiler
    Außerdem: Assemblercode kann man ziemlich gut warten, weil ja z.B. die Assemlerstruktur klar vor Augen liegt. In großen C oder anderen Hochsprachen-Programmen können viele Fehler gar nicht erst gefunden werden.
    Schwieriger wirds, wenn man extrem performant schreiben will. Da werden aber die meisten Programmiersprachcodes ziemlich unwartbar (sofern man das auf Codebasis lösen möchte und keinen weiteren, höher liegenden Ansatz (z.B. Algo) findet.

    Wurde leider aufgegeben:
    http://www.lowlevel.eu/wiki/Datei:Blue_Screen_V2OS.png



  • 8589934592 schrieb:

    hexkodierten Hintergrund

    😃
    $dff180 🕶



  • 8589934592 schrieb:

    Außerdem: Assemblercode kann man ziemlich gut warten, weil ja z.B. die Assemlerstruktur klar vor Augen liegt. In großen C oder anderen Hochsprachen-Programmen können viele Fehler gar nicht erst gefunden werden.

    The fail is strong with this one. 🙄



  • Gewisse Dinge lassen sich nur in ASM realisieren.
    Vor allem sehr hardwarenahe Sachen.

    Zum Beispiel muss das Betriebssystem in der Anfangsphase Paging (eine Form von virtuelle Speicher) aktivieren.

    mov eax, [page_directory]
     mov cr3, eax
    
     mov eax, cr0
     or eax, 0x80000000
     mov cr0, eax
    

    So etwas lässt sich in C nicht realisieren - denn dort hast du keinen direkten Zugriff auf Register.

    Außerhalb vom OS/Treiberbereich sehe ich heute aber wenig Einsatzgebiete für ASM.
    ASM zu verstehen ist gut, vor allem beim Debuggen haben mir halbwegs passable ASM Kenntnisse schon sehr geholfen.
    Dass ich aber tatsächlich in einer Firma ASM programmieren musste, ist mir noch nicht untergekommen.

    Aber weil du Audio erwähnst: Gerade DSP werden heute teilweise immer noch in ASM programmiert, um spezielle Prozessorfunktionen auszunutzen. Ansonsten fallen mir jetzt aber keine Jobbeschreibungen ein, wo ich mal was von ASM gekesen habe.


Anmelden zum Antworten