Beiträge von JeeK

    was mich mal interessieren würde: Wenn ich meinen Basic-Code optimiere und diesen dann durch einen Compiler übersetzen lasse. Ist der Maschinencode dann auch schneller, als wenn man einen nicht optimierten Code durch einen Compiler übersetzen lässt?

    In der Tat, kann man das wohl nicht allgemeingültig beantworten. Sagen wir so, es gibt sicher Fälle, wo das so ist.

    Wenn ich etwa so etwas habe:

    FOR I=0 TO 7: B(I) = 2^(7-I): NEXT

    und das optimiere zu

    V=1:FOR I=7 TO 0 STEP-1: B(I)=V: V=V+V: NEXT

    Das sind jetzt nur 8 Durchläufe, aber es wären ja umfangreichere Dinge auch möglich, wo das dann schon ins Gewicht fallen könnte.

    Also es hängt wohl von der konkreten Optimierung ab. Die Sachen, die "nur" die Interpreter-Laufzeit optimieren wirken sich beim Compiler nicht aus, alles was eher Richtigung algorithmische Optimierung geht schon eher.


    Zu einem gewissen Grad hängt das auch von den Optimierungsfähigkeiten eines Compilers ab.

    Ein

    IF A>0 AND A < 40 THEN ...

    wird mit

    IF A>0 THEN IF A < 40 THEN ...

    auch bei jenen Compilern schneller, die nicht eine bedingte Auswertung erkennen und von sich aus die Kaskade wie im 2. Beispiel erzeugen.

    Ich dachte das sei eine Qualität von Perl... :grinduckrun:

    Ne ne, Perl *kann* man gut verschleiern, muss aber nicht. Entgegen der landläufigen Meinung, ist Perl nicht so kryptisch, wie immer wieder kolportiert wird.
    Aber bei Forth müsste man bei jedem Wort-Aufruf (oder zumindest in regelmäßigen Abständen) in Kommentaren den Stack-Aufbau angeben, wenn man den Code auf einen Blick erfassbar haben möchte. Das ist aber eher selten der Fall (oder kratzt zu sehr an der Ehre eines Forth-Programmierers, wo vielleicht die Meinung vorherrschen könnte: "Die sollen sich beim Lesen des Codes genau so anstrengen, wie ich fürs Schreiben." :D). Da ist die Unübersichtlichkeit inhärent in der Programmiersprache. Ich sag immer, ohne jetzt Forth jemanden verleiden zu wollen, dass man mit Forth-Programmieren quasi einen Compiler "spielt" - diesen Schritt können Computer an sich viel besser. Ich vergleiche das gerne mit dem Dokumentschreiben, wenn das jemand in purem Postscript machen würde, obwohl die Verlockungen von WYSIWYG-Software allgegenwärtig sind. Aber umso mehr Respekt jenen gegenüber, die das dann tatsächlich so machen. :)

    Das ergänze ich noch: Mir gehts um Vektor-Graphik. Die Berechnungen gehen zwar in FORTH, aber um Himmels Willen, dazu wäre es so einfach, Variablen am Stack namentlich anzusprechen.

    Naja, das ist für mich die massive Schwäche von Forth, dass man da auf dem Stack gedanklich "mitführen" muss, sich die Positionen mitunter ständig ändern. Also für Parameterüber und das laufende Hantieren mit Werten mühsam und nicht wirklich elegant. Das was man selbst programmiert hat, versteht man am nächsten Tag nicht mehr intuitiv.
    Aber an sich: Variablen haben am Stack nichts verloren, außer man liest sie aus und deren Werte landen dann dort.


    Wobei ich mir grade überlege, ob ich das Ding nicht basteln könnte. Gibts in FORTH ein Wort, das einen Pointer auf das aktuelle Stack-Element zurückliefert? Dann ginge das wohl. Warum bin ich nicht schon vorher auf die Idee gekommen?

    Hmm - so wird eigentlich nicht mit Forth entwickelt bzw. ein Problem angegangen. Der Stack sollte eher nur dezent für solche Zwecke verwendet werden. Hier zwanghaft alles am Stack halten zu wollen, ist nicht zielführend - und es wäre megamühsam. Der Parameter-Stack ist - wie der Name schon sagt - primär für die Parameterübergabe. Üblicherweise sind Worte so kompakt und überschaubar, dass die Manipulationen meist direkt am Parameterstack gehen und dann auch gleich das Ergebnis dort liegen kann. Wenn es komplexer wird, wird der Return-Stack zu Hilfe genommen. Aber das ist dann auch nicht mehr wirklich "schön".

    Dafür ist der Stack auf einer 6502-Architektur meist auch zu klein, dass man diesen allzu extensiv nutzen könnte. Also mit einem Pointer auf einem Stack-Element zu operieren, konterkariert das Forth-Paradigma aus meiner Sicht - ich hätte das so auch noch nicht in der Praxis gesehen. Daten und Werte legt man in Datenstrukturen, wie eine (vorgebene) Variable, Array oder definiert sich selbst entsprechenden Datenstrukturworte (Definitionsworte), etwa 3D-Koordinaten oder so, oder eine Kantenliste/Kanten-Array etc. Dann hat man am Stack bei der Übergabe bestenfalls die Pointer auf das jeweilige Datenelement (oder Struktur). Das geht dann eher in Richtung Objektorientierung für umfangreichere Sachen Definitionsworte könnte man grob als Klassen mit einer einzigen Methode sehen, eine dynamische Objektverwaltung fehlt allerdings dabei - also der Vergleich hinkt natürlich etwas, aber so im Groben kann man sich das ganz gut gedanklich modellieren, wenn einem das objektorientierte Paradigma geläufig ist). ;)

    Grundsätzlich ja, aber eine unterschiedliche Zahlenlänge kann das offenbar auch sehr schnell wieder ausgleichen, d.h. wenn man z.B. "*0.25" hat, sollte man das lieber mit "/4" ersetzen.

    Oder *.25 schreiben oder noch besser 0.25 einer Variablen zuweisen und man spart sich die Umwandlung in das Floating-Point-Format (sofern nicht die Variablensuche im Gegenzug zu lange dauert). ;)

    ich hab mir überlegt, da ich in FORTH und C nicht wirklich weiter gekommen bin,

    Oh, was ist passiert? Was hat das "Weiterkommen" gehindert?

    Ich geb zu, Forth ist halt wirklich etwas "kantig" und wenn man nicht wirklich wirklich völlig der Forth-Idee verschreibt, dann tut man sich schon schwer. Ich hab eigentlich für meine paar Herumprobierereien das Performance Micro Products (eine Forth/79 Variante) mit meinen Grafikbefehlen verwendet.
    Moderner und eleganter wäre vermutlich Durex-Forth, oder?

    Bei C lass ich mir das schon gut vorstellen. Wenn man nicht wirklich eine Cross-Entwicklung macht, dann ist das auf dem C64 direkt nicht wirklich lustig.

    Die Lösung war viel einfacher. Es gibt nur 65336 mögliche Sprungadressen. Das entspricht einem Array von 4196 Integer-Werten. Davon zwei Stück (eins für Programm- und eins für Datenlabels) und dann einfach immer das entsprechende Bit gesetzt bzw. abgefragt. Keine Sucherei mehr. :thumbup:

    Korrekterweise würd ich meinen 4096, oder?

    Ja, ich liebe solche Tabellenlösungen auch. In dem Fall braucht man halt 16 KByte Array-Speicher, wenn man den natürlich hat, dann umso besser. :D

    Das artet für mich jetzt in ein unnötiges Hick-Hack aus, wie jemand etwas verstehen (oder nicht verstehen) will. Ich finde Snoppys Beträge dazu valide und auch wenn man sie nicht akzeptabel (oder ungerechtfertigt) finden sollte, bringt es auch nichts, ständig dessen "Legitimität" irgendwie absprechen zu wollen bzw. ihn oder uns hier davon zu überzeugen, wie falsch er mit seiner Meinung liegt. Dabei hat Snoppy ja Bernis Arbeit aus meiner Sicht in keinster Weise geschmälert, sondern nur seine Einschätzung dazu abgegeben, was wohl jedem zugestanden werden kann und sollte (natürlich ist auch die Kritik dieser Einschätzung auch wiederum valide, aber da landen wir dann wieder beim besagten Hick-Hack).
    Und so viel liegen eure Sichtweisen ja nicht auseinander, abgesehen davon, andere Wörter und Analogien zu verwenden und sich an deren Verwendung aufzuhängen. Also insofern hat diese Art der Diskussion eher am wenigsten zum Ganzen beigetragen (wenn man schon über "für die Katz" spricht). ;)

    Ich meinte die Einrückungen. Bei vielen C64 BASIC-Programmen wird ja versucht, möglichst viele FOR...NEXT Schleifen in eine Zeile zu quetschen. Warum auch immer.

    Typischerweise aus Geschwindigkeitsgründen. Der "schöne" Code schlägt sich halt leider auf die Laufzeit. Aber: Oftmals könnte man sich da auf kritische Abschnitte beschränken und sonst schön "coden", aber zumindest als man noch direkt auf C64-Hardware entwickelt hat, war man halt mit dem Bildschirmeditor oder ohne sonstige Tools eher in einer schlechten Position, ein Programm über mehrere Entwicklungszyklen hinweg lesbar zu gestalten - im Gegenteil, es ist eher immer grauslicher geworden. ;)

    Das würde ich jetzt nicht als Bug bezeichnen. Das ist eine Eigenschaft des Tokenizers. Wenn man denn mit einem "unerlaubten" Zeichen einen Variablen unterjubeln kann (verhindert es als Token zu sehen), dann ist es auch gut und eben eine Eigenschaft des Tokenizers. Der Interpreter arbeitet hingegen völlig korrekt.
    Das geht auch unter BASIC V2

    Code
    1. 10 I F=10
    2. 20 IF I F=10 THEN PRINT "HUHU"

    Das LIST zeigt halt das Leerzeichen weiterhin an, mit einem Sonderzeichen (PETSCI 164, wenn man Underscore über VICE eingibt) verschwindet es dann auch.


    Dabei wird weder I noch F als Variable verwendet, sondern wirklich IF (im Monitor überprüft).

    Also PRINT I F oder PRINT I_F zeigen dann 10 an.

    Nach dieser umwerfenden Kritik ziehe ich mich aus diesem Thema komplett zurück, mache gar nichts mehr weiter und werde wahrscheinlich dann auch, alles was ich entwickelt habe, komplett löschen...

    Bitte lass dich doch nicht von etwas Unempathischer Kritik nicht entmutigen. Ich weiß nicht welchen Tag die jeweiligen Personen gerade hatten, aber ich würde euch nur ermuntern, die positiven Dinge hervor zu heben und es nicht in einen Wettstreit ausarten zu lassen. Niemand ist perfekt und vielleicht können die Ideen einander ergänzen und sich nicht abwürgen.
    Ich finde Neo64 Intention sehr löblich und er tut sich wirklich Einiges an, wo man ruhig in Kauf nehmen kann, dass es nicht in jeder Hinsicht optimal ist. Da kann man auch durchaus sanft den Weg zu anderen Richtungen zeigen, ohne das eine gewisse Herablassung mitschwingt (oder als solche aufgenommen werden kann, auch wenn man es nicht so gemeint haben mag).
    Ich schätze auch immer die Beiträge von atomcode und vielleicht können sich die Ideen und Intentionen sich so einschwingen, dass der jeweils andere nicht das Gefühl vermittelt bekommt, quasi als Stümper (überzogen) gesehen oder nicht ernst genommen wird.

    Wir frönen doch alle dem gleichen Hobby und wollen Spaß und ein bisschen Anerkennung dabei haben.

    Vielleicht können wir wieder alle an einem Strang bzw. den Wagen in die gleich Richtung ziehen ... :emojiSmiley-05:

    Oh, das überrascht mich. Als Laie dachte ich, das Nachschlagen der Konstante müsste aufwendiger sein, als das direkte rechnen mit 255? :emojiSmiley-15:

    Die Konstante 255 muss relativ aufwendig erst in eine Fließkommazahl konvertiert werden. Da ist das Holen aus einer Variable insofern schneller, als dass dort der Wert schon als Fließkommazahl vorliegt. Wenn die Variable nicht zu lange gesucht werden muss, ist das dann merklich schneller.
    Das zahlt sich nicht für alle Konstanten aus (besonders bei einstelligen eher nicht), da muss man sich die häufigsten oder besonders in Schleifen oft verwendeten Werte herausgreifen. Klassiker sind dann schon größere Zahlen, wie 8192, 32768, 65536 usw., wenn man mit Adressen herumrechnet o.ä.

    Was heißt Probleme, das glaube ich eher nicht. Funktionieren dürften Deine Konstrukte auch compiliert.

    Das ist für Basic richtig ausgefuchste Programmierung, die aber aus Compiler-Sicht nichts bringt. Ein GETA$:IFA$<>""THEN oder alternativ POKE198,0:WAIT198,1 ist in einem compilierten Programm einfach effektiver.

    Bei einem Warten auf Tastendruck macht das keinen Unterschied, wenn man zeitkritische Routinen baut, schaut das schon wieder anders aus.

    Generell kann man eigentlich sagen, wenn man ein Programm irgendwann mal durch einen Compiler jagen will, sollte man das bei der Basic-Programmierung schon entsprechend berücksichtigen.

    Ah, ok, das meinst du. Ja klar, stimmt.
    Aber etwas zu kompilieren, um es schneller zu bekommen ist ja generell etwas anderes. Für mich persönlich ist eher unsportlich. ;) Wenn man BASIC als Compilersprache sieht (oder sein Projekt dahingehend ausrichtet) und verwendet, dann ist ja jegliche Optimierung für einen Interpreter offensichtlich überflüssig. Aber da sind schon einige Annahmen, von den ich standardmäßig nicht (immer) ausgehe: BASIC läuft auf einem C64 (wenn nicht explizit auf den C64 zugeschnitten) und es soll kompiliert werden.

    Heute ist die erste Version vom kombinierten BASIC & KERNAL Patch-Paket fertig.


    Dieses Patch-Paket kopiert das BASIC & KERNAL ROM in das RAM und führt die folgenden 6 Patches aus:
    [..]
    Garbage Collection: Korrigiert und ersetzt den Code der Garbage Collection und beschleunigt diese um den Faktor 10-33.

    Wichtig dabei zu erwähnen ist, dass diese Implementierung einen String-Overhead von 2 Bytes pro String am String-Heap hat. D.h. Ein String der Länge 1 verbraucht damit 3 Bytes am String-Heap. Bei gewissen (großen) Programmen, die viele aktive Strings unterhalten, kann das schon einen entscheidende Rolle spielen, auch wenn das "Aufräumen" blitzschnell geht. In aller Regel, wird es aber eher selten zu Problemen kommen.
    Das ist übrigens die gleiche Speicherorganisation, wie sie auch bei BASIC 3.5 (264-Linie) oder BASIC 7.0 (C128) und natürlich auch BASIC 4.0 (PET) vorliegt.

    Verwendet wurde für den hier nachfolgenden Garbage-Collection-Patch der Patch-Code in github-Repository unter "cbm-gc/src/GARBCOL/optimized-2/", also der zuletzt korrigierte und optimierte Code. Aufgrund der Code-Größe wurde auch die Einschaltmeldung verkürzt und es waren auch noch kleine Code-Anpassungen aufgrund der Größe und der Platzierung im ROM notwendig.

    Nur zur Richtigstellung:

    Das (optimized-2) ist die falsche Referenz und nicht die letzte Version, sondern bitte direkt auf https://github.com/JeeeK/CBM-GC/tree/master/src/GARBCOL verweisen. Es wird zwar (zufällig) der gleiche Code erzeugt, aber die Optimierungen sind von 4 auf 3 korrigiert worden, denn eine der Optimierungen (die aber immer deaktiviert war) hätte einen ernsthafte (negative) Auswirkung auf die String-Addition gehabt und ist im aktuellen Hauptzweig keine Option mehr. ;)

    Aus diesem Grund darf man in Pascal FOR-Schleifen nur mit Ordinaltypen machen. Dann kommt man erst gar nicht zu solchen Problemen. Bei C und Java und Konsorten muss man schon genau wissen, was man tut. Da sollte man bei Abbruch-Bedingungen bei for-Schleifen mit double oder float auf keinen Fall einen Test auf gleich benutzen, immer nur auf größer / kleiner.

    Genau, deswegen ist das keine Legitimation, bloß weil man etwas programmieren kann, dass es laufzeittechnisch auch wirklich vernünftig ist. Gerade bei archaischen BASIC 2.0 gibt es etliche Freiräume, die betreten werden können, aber ein nicht vorhersehbares Verhalten hervorrufen. Dieser Freiräume und deren Konsequenzen einzuschätzen, beleuchtet eben die Numerischen Datenverarbeitung/Mathematik.

    Zudem darf man nicht der Illusion erliegen, dass ein Computer "perfekt" ist. Da war man in den 80ern vielleicht am Anfang noch naiv, aber heute wissen wir es mittlerweile bessern ... :D

    Wenn der STEP-Wert positiv ist, wird (beim NEXT-Befehl) abgebrochen, sobald der Schleifenwert größer als der Endwert ist, wenn der STEP-Wert negativ ist, wird abgebrochen, sobald der Schleifenwert kleiner als der Endwert ist. Bei FORI=3TO1 ist der (implizite) STEP-Wert 1, also positiv. Es wird also beim FOR I=3 gesetzt, dann bis zum NEXT die Befehle ausgeführt. Dort wird I=I+1 ausgeführt und dann I>3 überprüft. Da das der Fall ist, wird nicht zurück zum FOR gesprungen.

    Es gibt noch den Sonderfall STEP 0: Hier muss genau der Endwert erreicht werden. ;)

    Da hatte ich mich verlesen. Ich hab nicht gesehen, dass Du eine Endlosschleife bauen willst. In BASIC V2.0 ist eine Endlosschleife leichter ohne FOR zu erreichen. Aber vielleicht hat ja einer eine Idee.

    Aber die FOR-NEXT-Schleife ist üblicherweise die schnellere Variante.


    10000 FOR I=0 TO 1 STEP 0

    ...

    10100 NEXT


    Das NEXT ist hier in der Regel in einem Programm mit mehreren Dutzend Zeilen dann deutlich schneller als ein GOTO zu niedrigeren Zeilen.


    Man kann den den Endwert dann auch für eine Abbruchbedingung nutzen: (Dateien lesen)


    100 FOR S=0 TO 1

    150 INPUT#1,A$: PRINT A$

    190 S=ABS(ST)

    200 NEXT


    Bei Fehler oder End-of-File ist der Status ist der Wert >=1 (der Status kann auch -128 sein, deswegen das ABS()) und dann endet die Schleife. Sonst wird immer wieder auf S auf 0 gehalten und das NEXT kommt nicht über 1.


    Oder Tasteneinlesen:


    1000 FORA=0TO1:GETA$:A=LEN(A$):NEXT


    Gegenüber zeitlich invariant gegenüber Lage im und Größe des Programms (kurze Reaktionszeit bei Spielen etwa) und weniger fehleranfällig als mit der Zeilennummer bei GOTO herumzuhantieren.