Hello, Guest the thread was called103k times and contains 1123 replays

last post from ZeHa at the

Heute so gecodet...

  • Crunchen die Programme auf dem Amiga mit einer 24Bit Adressierung, also Bankübergreifend (16MB) oder nur 64KB auf einmal.

    Auf dem Amiga gibt es nur ein Flat Memory Model. Segmente oider Banking gibts da keines. Das ist das schöne an der M68000 CPU. ;) Bei Intel gibts das ja im Prinzip eigentlich erst ab dem 80368 mit Protected Mode.

  • Ok, Danke! Dann werde ich mir mal den Levelcrusher Disasseblieren und diesen an meine Wünsche anpassen. Eine üble Arbeit, und es dauert bis es fertig ist. Aber wenn es funktioniert, bin ich happy.:)


    Beim Exomizer gibt es übrigens den RAW-Modus. Damit ist es möglich, Dateien die länger als 64KB sind, zu (level)crunchen.

    Nur habe ich zu wenig Information gefunden, wie eine RAW-Gecrunchte Datei decruncht wird.


    Falls es interessiert, die Maniac Mansion Supercpu Version ist Exomizer 64kb gestückelt Levelgecruncht. Ungecruncht 4064 Blöcke und gecruncht 1685 Blöcke.

    Ich habe auch den Rasterzeileninterrupt am die 20Mhz angepasst. Also kein langer Strich am unteren Bildschirmrand. Das ist nur eine Spielerei von mir.

  • Gestern Abend hatte ich so logische Ausdrücke mit diesem manchmal nützlichen Logikrechner optimiert. Allerdings hatte ich dann die optimierten Versionen doch nicht benutzt. Denn, wenn man seinen Code im Nachhinein noch verstehen will oder muss, ist das nicht unbedingt sinnvoll, Ausdrücke zu haben, wo man nicht mehr die geringste Vorstellung hat, worum es da geht. Selbst, wenn es in Assembler um die Ersparnis von Rechenzeit und Speicher geht, sind als Vorlage die natürlichen längeren Ausdrücke mit etwas mehr boolschen Operationen und etwas Redundanz und besserer Homogenität oft einfacher und effektiver umzusetzen als die entsprechenden "optimierten" Versionen. So meine Erfahrung, die sich gestern wieder deutlich bestätigte.


    Beispiel: // WO, WR, WU, WL = Wände oben, rechts, unten, links; RO, RU, LU, LO = Ecken


    Normale Aussage (15 Verknüpfungen):

    (WO AND WU) OR (WR AND WL) OR (WL AND WO AND RU) OR (WO AND WR AND LU) OR (WR AND WU AND LO) OR (WU AND WL AND RO)


    Was sagt mir das?

    (WO AND WU) OR (WR AND WL) = Wenn sich zwei Wände gegenüberliegen ..

    OR (WL AND WO AND RU) OR (WO AND WR AND LU) OR (WR AND WU AND LO) OR (WU AND WL AND RO) = .. oder zwei Wände einer Ecke gegenüberliegen.


    Und was sagt mir die "Optimierung" (12 Verknüpfungen)? Nicht mehr viel.

    (WU AND WL AND RO) OR (WO AND ((WR AND LU) OR WU OR (WL AND RU))) OR (WR AND (WL OR (WU AND LO))) = ???

  • Also, ich programmiere gewiss viel weniger und viel schlechter als Du, aber (oder deswegen?) würde ich mir, wenn's auf die Optimierung ankäme, die unoptimierte Variante in eine Kommentarzeile schreiben...?! :nixwiss:

  • ...wenn's auf die Optimierung ankäme, die unoptimierte Variante in eine Kommentarzeile schreiben...?! :nixwiss:

    Oder beides verwenden indem man eine DEBUG-Konstante verwendet.



    Beispiel:


    #If DEBUG Then

    _Einfache_Version_

    #End If


    #If RELEASE Then

    _Optimirte_Version_

    #End If

  • wenn's auf die Optimierung ankäme, die unoptimierte Variante in eine Kommentarzeile schreiben

    Das mache ich wohl so, sobald es sich lohnt. Es ist in diesem Fall übrigens C64-Programmierung. Ich programmiere das in BASIC so vor, wie es für die Umsetzung in Asm optimal ist. Dabei ist die BASIC-Geschwindigkeit nicht so wichtig, sondern die in Asm zu erwartende Struktur und somit die Geschwindigkeit und der Speicherumfang. Das heißt, diese Optimierung ist in solchen Fällen ohnehin nicht hilfreich, auch wenn es ein paar logische Verknüpfungen weniger sind. Allein die vielen geschachtelten Klammerpaare lassen erahnen, dass man häufiger zwischenspeichern muss. Darüber hinaus ist aber auch die Übersicht der Vorlage wichtig. Wenn der Code also gut umgesetzt werden kann und zudem noch verständlich ist, kann ich mir die Remarks sparen.


    Sofern eine Optimierung bzgl. der Rechenkapazität einen Nutzen bringt, würde ich diese verwenden und dann auch dahinter schreiben, worum es da geht, als verständlichen Code und/oder Klartext.


    Denn, wenn man seinen Code im Nachhinein noch verstehen will oder muss, ist das nicht unbedingt sinnvoll, Ausdrücke zu haben, wo man nicht mehr die geringste Vorstellung hat, *worum es da geht.

    *wie der Code das macht. Wenn man bei solchen Optimierungen den ursprünglichen Code und noch eine Erklärung im Klartext als Remark mitführt, versteht man den Code ja immer noch nicht im Detail. Man weiß zwar, was da passiert oder abgefragt wird, aber man weiß nicht mehr, wie und warum der Code das macht, weil es zu kryptisch geworden ist. Der eigene Code ist plötzlich fremd; das will ich vermeiden.


    ---

    Ich hatte mal überlegt, ob es nicht sinnvoll wäre, den Coding-Thread genauso zu splitten wie den Gaming-Thread:


    • "Was spielt Ihr derzeit so am C64?" => "Was codet Ihr derzeit so für den C64?" oder "Heute so gecodet (C64)"

    • "Was spielt ihr so? (allgemein)" => "Was codet Ihr so (allgemein)?" oder "Heute so gecodet (allgemein)"

  • Also, ich programmiere gewiss viel weniger und viel schlechter als Du, aber (oder deswegen?) würde ich mir, wenn's auf die Optimierung ankäme, die unoptimierte Variante in eine Kommentarzeile schreiben...?!

    Genau so macht man das auch als guter Programmierer (oder sollte man zumindest machen) - man vergisst schnell was man sich da gedacht hat ;)


    Und Kommentare tun nicht weh - ganz im Gegenteil :thumbsup:


    Davon würde ich Dir dringend abraten!!

    Du holst Dir so nur unnütz mehr Arbeit in dein Projekt und hast deutlich höheren Wartungsaufwand!!

    Auch ist dieses Vorgehen deutlich fehleranfälliger - man ändert so schnell mal was nur an einer Stelle um kurz etwas zu testen und wundert sich dann wieso es bei Release auf einmal fehlerhaft läuft.


    Von so einem Konstrukt bitte absehen...

  • Davon würde ich Dir dringend abraten!!

    Du holst Dir so nur unnütz mehr Arbeit in dein Projekt und hast deutlich höheren Wartungsaufwand!!

    Auch ist dieses Vorgehen deutlich fehleranfälliger - man ändert so schnell mal was nur an einer Stelle um kurz etwas zu testen und wundert sich dann wieso es bei Release auf einmal fehlerhaft läuft.


    Von so einem Konstrukt bitte absehen...

    Also im konkreten Fall, zur Auswahl einer einfachen/verständlichen Version oder einer schnellen/unverständlichen Version, stimme ich gerne zu. Wobei es an der Stelle aber nahe liegend ist, beide Versionen zu berechnen und zu vergleichen, was dann schnell zu einem "Unit test" wachsen kann.


    Aber so ganz generell finde ich für IF DEBUG oder IF IDE doch sehr nützliche Anwendungen, auf die ich nicht verzichten wollte.

  • Davon würde ich Dir dringend abraten!!

    Du holst Dir so nur unnütz mehr Arbeit in dein Projekt und hast deutlich höheren Wartungsaufwand!!

    Auch ist dieses Vorgehen deutlich fehleranfälliger - man ändert so schnell mal was nur an einer Stelle um kurz etwas zu testen und wundert sich dann wieso es bei Release auf einmal fehlerhaft läuft.


    Von so einem Konstrukt bitte absehen...

    Oh, mit so einer heftigen Reaktion hätte ich jetzt nicht gerechnet. :)


    Sehen das andere Coder auch so? Ich werde davon aber nicht abrücken, genauso wenig wie von globalen Variablen in vb.net oder dem Ausschalten von Option Strict im Visual Studio. :whistling:

  • Sehen das andere Coder auch so?

    Du musst damit zurecht kommen, solange du nicht in einem Team dran arbeitest, daher ist alles erlaubt was dir gefällt. Bei optimierten Sachen, also in diesem Fallen die Anwendung von Demorgansche Gesetzen oder so was, würde ich die lesbare Form als Kommentar drüber schreiben. Jeder hat halt so seine Stil im Laufe der Zeit entwickelt.

  • Sehen das andere Coder auch so?


    Ich sehe das genauso wie "derSchnippe", solche Konstrukte sollte man nicht dazu verwenden, eine gänzlich andere Logik zu implementieren. Wenn, dann würde ich für sowas ein eigenes Define nutzen, etwa "IF_OPTIMISED" oder etwas in der Art, aber auf keinen Fall die DEBUG/RELEASE Defines dafür missbrauchen.

  • Oh, mit so einer heftigen Reaktion hätte ich jetzt nicht gerechnet. :)


    Sehen das andere Coder auch so?

    Ja. Erstens ist es blöd zu maintainene und nach einiger Zeit vergisst man auch warum man das so gemacht hat. Ausserdem hast du zwei verschiedene Codeteile die eigentlich das gleiche machen. Wenn du an einem rumschraubst vergisst du mit Sicherheit das im anderen entsprechend nachzuziehen.

  • Allein die vielen geschachtelten Klammerpaare lassen erahnen, dass man häufiger zwischenspeichern muss.

    Nein, eher nicht. Ein Ausdruck wie A + (B * (C + (D * (E + (F * (G + (H * I))))))) mag kompliziert aussehen, lässt sich aber - wenn man von innen nach außen rechnet - auf Assembler-Ebene ganz leicht sequenziell verarbeiten.


    Deine Beispiele im Vergleich:

    (WO AND WU) OR (WR AND WL) OR (WL AND WO AND RU) OR (WO AND WR AND LU) OR (WR AND WU AND LO) OR (WU AND WL AND RO)

    Hier berechnet man die ersten fünf Klammerausdrücke und speichert die fünf Ergebnisse ab. Dann berechnet man den sechsten Klammerausdruck und fährt direkt fort, indem man die fünf zuvor gespeicherten Ergebnisse aufODERt.


    Jetzt die optimierte Variante (der aktuelle Rechen- und Speicherschritt ist unterstrichen):

    (WU AND WL AND RO) OR (WO AND ((WR AND LU) OR WU OR (WL AND RU))) OR (WR AND (WL OR (WU AND LO)))

    Schritt 1:

    (WU AND WL AND RO) OR (WO AND ((WR AND LU) OR WU OR (WL AND RU))) OR (WR AND (WL OR (WU AND LO)))


    Schritt 2:


    (WU AND WL AND RO) OR (WO AND (TEMP1 OR WU OR (WL AND RU))) OR (WR AND (WL OR (WU AND LO)))


    Schritt 3:


    TEMP2 OR (WO AND (TEMP1 OR WU OR (WL AND RU))) OR (WR AND (WL OR (WU AND LO)))


    Nach dem Speichern von nur drei Zwischenergebnissen liegt die Formel dann bereits in dieser Form vor:

    TEMP2 OR TEMP3 OR (WR AND (WL OR (WU AND LO)))

    Die kann man jetzt von innen nach außen in einem Rutsch ausrechnen.

  • Nein, eher nicht.

    Der Meinung bin und bleibe ich sehr wohl. Und bitte den Kontext beachten, also dass das keine Allgemeingültigkeit hat!


    Wenn ich deine Doktorarbeit zu dem optimierten Ausdruck sehe, bekomme ich die Krise. Das wäre wirklich nach dem Motto "Warum einfach, wenn es auch kompliziert geht". Da weiß man nicht mehr, was genau man da überhaupt tut, fängt mitten drin an anstatt von vorn und kann sich schneller mal mit den ganzen geschachtelten Klammern vertun, während die Klammern in der humanlogischen Version lediglich der Übersicht dienen und semantisch nicht mal nötig wären. btw Bis jetzt habe ich für den C64 noch keinen Debugger gebraucht, und das kann gern so bleiben.


    Was besseres als eine OR-Sequenz kann man gar nicht haben, weil man bei einer Erfüllung sofort raus ist aus dem Geschehen und bei diesem Beispiel auch überhaupt keine Zwischenwerte speichern muss; es reicht allein der Akku. Es brauchte genau 22 Befehle mit durchschnittlich 35 Taktzyklen pro Gesamtauswertung, bei einer übersichtlichen, homogenen und idiotensicheren Ablaufstruktur, die keiner Zusatzerklärung bedarf - im Gegensatz zum Asm-Pendant aus dem vermeintlich optimierten Konstrukt. Ausprobieren werde ich letzteres nicht; kann ja wer anders machen, wenn er meint, es mit weniger Befehlen/Zyklen und ohne Zwischenspeicherung hinzubekommen.


    selbsterklärend, braucht keine Remarks (Giga-Ass)

  • Klasse! Wieviele FPS schaffst du so?


    Mit dieser ungeheuren reinen Rechenkraft heutzutage müssten da doch mindestens zweistellige FPS bei rumkommen, oder?

    Das sind sehr gute Fragen. Eine FPS Messung habe ich gar nicht eingebaut, kommt aber sicher, wenn ich für meine Verhältnisse "fertig" bin. Bis jetzt render ich nur alle 20ms ein Frame über ein SDL TimerEvent.


    Superschnell wird mein Code bis dato nicht sein. Ist zwar reines C, aber ich habe algorithmisch null optimiert, da kann man sicher viel vor berechnen, oder auch Fließkomma durch FixedPoint ersetzen(weiß aber nicht ob das bei heutigen CPUs wirklich schneller ist), zusätzlich wäre sicher auch noch Assembler mit den aktuellen Befehlserweiterungen interessant usw. Aber an die billigste GPU werde ich natürlich nie auch nur annähert ran kommen. Gibt ja einen guten Grund warum die es selbst in die billigsten Handys geschafft haben.


    Bei mir fehlt auch noch enorm viel, wie Zbuffer, 2D und 3D Clipping, Culling, Bump-Mapping, eventuell Schatten, 3D Objekt Loader. Was schon fertig ist, man aber nicht so richtig sieht, sind mehrere Lichtquellen und die Kamera mit Zielvektor.


    An längsten habe ich mit dem Dreiecks Rasterer verbracht. Ich habe entweder Interpolationsfehler gehabt, oder eben Lücken zwischen Kanten der Dreiecke die aneinander liegen. Das sind so Rundungsfehler, die ich jetzt so hingebogen habe, dass es schön aussieht und nur in extremen Fällen sieht man mal was ungenaues. Perfekt ist das nicht und ein wenig wurmt mich das auch, aber irgendwann ist halt gut auch gut genug und man muss weiter machen.


    Dankbar bin ich Visual Studio für das gute Refaktoring, wie oft ich schon Variablen- und Funktionsnamen im kompletten Projekt umbenannt habe, geht auf keine Kuhhaut und der Debugger hat mich auch schon oft den Arsch gerettet.


    Hier mal ein Gif wie es animiert aussieht, ist natürlich in echt flüssig und mit mehr Farben:

  • Also solange man den Parameter zum steuern von DEBUG oder RELEASE von aussen in den BuildProcess einfliessen lässt durch ein CI/CD Tool um dann passend zu deployen ist das völlig in Ordnung. So kann da im Grunde auch nicht viel passieren.

  • Oh, mit so einer heftigen Reaktion hätte ich jetzt nicht gerechnet. :)


    Sehen das andere Coder auch so? Ich werde davon aber nicht abrücken, genauso wenig wie von globalen Variablen in vb.net oder dem Ausschalten von Option Strict im Visual Studio. :whistling:

    Das große Problem wurde ja schön geschildert: 2 Programmteile, die das Gleiche machen SOLLEN, aber irgendwann im Laufe der Jahre geht die Pflege schief, und sie machen was unterschiedliches.

    Im konkreten Fall, so 1:1, wie Du ihn geschildert hast, hilft es eigentlich gar nicht: Während des Debuggings nutzt Du eine verlässliche Routine, aber die Release-Version wird dadurch nicht besser oder vertrauenswürdiger.


    Was ich vergleichbares an Flags in meinem Programmierer-Leben habe:

    Ein DEV-Flag (durch IDE erkennen oder Parameter).
    - Manchmal, um Quick and Dirty mit bestimmten Datensätzen statt echter Abfragen zu testen, Plausi-Prüfungen zu umgehen o.Ä.

    - manchmal, um damit Fehlerbehandlungen abzuschalten. Fehler-Popups sind einfach praktischer als Mails ans Ticket-System.


    Ein OPTIMIZE-Flag, wie es Stingray glaub ich meint. Für Code, dem man nicht recht vertraut, wie im Beispiel.

    - Im besten Fall schaltet es weitere, langsame Plausi-Prüfungen oder Log-Meldungen frei, im Beispiel einen Vergleich mit der Original-Formel.

    - In komplizierten Fällen bekommen Anwender auch mal 2 Menüpunkte (Alt / Neu, Test). Man erreicht nicht immer Einigung darüber, was richtig oder falsch ist. Und falls es falsch ist, bleibt dem Anwender noch eine Alternative.


    Ein ähnliches Flag für Datenaustausch mit Kunden.

    Manchmal wollen Schnittstellen zu bestimmten Zeitpunkten scharfgeschaltet werden, wenn man schon längst zur nächsten Baustelle weitergezogen ist. Dann schleppt man halt für eine Weile den alten Code mit.