Ich habe die Geschwindigkeit eines TI-84-Emulators um das 10-fache erhöht, indem ich eine Switchbox ersetzt habe

2022-08-07

Es gibt einen Javascript-Emulator für die Rechner TI83+, TI84+ und TI84+CSE namens jsTIified, der von Christopher Mitchell, Gründer der Rechner-Fansite cemetech.net, geschrieben wurde. Es gibt nicht viel Grund, es für etwas anderes zu verwenden, wenn Sie eine native Option zur Verfügung haben, aber wenn Sie dies nicht tun, ist das ziemlich gut. Mein Interesse war geweckt, weil es der erste Emulator war, der den TI84+CSE unterstützte, als dieser Rechner in den frühen 2010er Jahren veröffentlicht wurde. Der CSE war aufregend, weil er einen 320x240-Farbbildschirm auf der Hardwareplattform +SE des 84 nachrüstete, also alle andere Hardware und Betriebssysteme Der Zugriff war bis auf die Grafiken derselbe. Ich wollte ein früher Spieleentwickler für die CSE werden, aber das Entwickeln für den Taschenrechner ohne Emulator oder Debugger ist ziemlich mühsam, also habe ich jsTified mit meinen alten Taschenrechner-ROMs versucht, um ein Gefühl dafür zu bekommen.

jsTified hatte jedoch ein Problem: Es war zu langsam. Diese Rechner verwenden einen z80-Prozessor, der recht einfach zu emulieren ist. Aber jsTified konnte nicht einmal 6-MHz-Rechnermodelle mit voller Geschwindigkeit emulieren, und die CPU des CSE war mit 15 MHz getaktet, also war es noch schlimmer. jsTified ist Closed Source, aber ich habe beschlossen, trotzdem etwas dagegen zu unternehmen.

Das erste, was Sie beim Debuggen einer Webanwendung tun möchten, ist natürlich, auf den Profiler zuzugreifen. Es gab nur einen Hotspot, der alle anderen in den Schatten stellte, und das war der Befehlsdecodierungs- und -ausführungsschalterblock. Das ist so, wie man es erwarten würde, da diese Rechner keine andere komplizierte Hardware wie Pixelverarbeitungseinheiten oder Audiochips zum Emulieren haben, aber es schien ein bisschen zwielichtig zu sein. Ja, Javascript ist langsam, aber Computer, die in den frühen 2000er Jahren hergestellt wurden, hätten die Emulation dieses Taschenrechners mit nativem Code in voller Geschwindigkeit bewältigen können. Javascript-Überlastung war nicht genug, um es zu erklären.

Also fing ich an, mich mit dem eigentlichen Code zu beschäftigen. Ich musste es deminifizieren, aber ich hatte früher mit verschleiertem Minecraft-Code zu tun. Der Befehlsdekodierblock hatte einen riesigen Schalterblock mit zusätzlichen verschachtelten Schalterblöcken für Multibyte-Befehle. In den meisten Sprachen ist das in Ordnung, da Ihr Compiler es in Sprungtabellen umwandelt, also warum habe ich hier keine Sprungtabellenleistung gesehen? Ich war damals aufgrund meiner WebGL-Erfahrungen ein wenig besessen von der Leistung von Javascript und hatte bereits gelernt, dass damals JS-Engines Funktionen nicht über eine bestimmte Größe hinaus optimierten. Mit diesem Wissen teilte ich alle verschachtelten Switch-Anweisungen in ihre eigenen Funktionen auf und bat den übergeordneten Switch, sie aufzurufen, um zu sehen, ob das alles erledigen würde.

Jetzt brauchte ich eine Möglichkeit, meinen Code zu laden. Ich habe schnell einen Webserver auf meinem Computer erstellt, der Anfragen an die Upstream-Website weiterleitet, aber die Anfrage von der Emulations-Engine abfängt und stattdessen meinen modifizierten Code zurückgibt. Ich habe /etc/hosts umgestellt, um die Upstream-Domain auf 127.0.0.1 zu verweisen, und mein Code wurde geladen.

Leider habe ich fast keine Beschleunigung gesehen. Ich war mir zu diesem Zeitpunkt ziemlich sicher, dass ich mich innerhalb der Größenbeschränkungen für die Funktionen befand, also muss etwas anderes fehlen. Ich suchte nach Low-Level-Details zur Implementierung von Switch-Anweisungen in Javascript. Schließlich fand ich einen Stackoverflow-Beitrag von jemandem, der genau dasselbe versuchte wie ich: einen (anderen) z80-Emulator zu optimieren. Da sah ich einen zutiefst beunruhigenden Kommentar mit Quellen, die direkt aus dem V8-Quellcode von Chrome zitiert wurden:

@LGB tatsächlich in V8 (JS-Engine, die von Google Chrome verwendet wird), müssen Sie viele Schritte durchlaufen, um das Schaltergehäuse zu optimieren: Alle Gehäuse müssen vom gleichen Typ sein. Alle Fälle müssen entweder String-Literale oder 31-Bit-Integer-Literale mit Vorzeichen sein. Und es müssen weniger als 128 Fälle sein. Und selbst nach all diesen Reifen erhalten Sie nur das, was Sie mit if-elses sowieso bekommen hätten (dh keine Sprungtabellen oder ähnliches). Wahre Geschichte.

Überprüfen Sie den Beitrag selbst hier https://stackoverflow.com/questions/18830626/should-i-use-big-switch-statements-in-javascript-without-performance-problems#comment27798374_18830724

Nicht das, was Sie hören möchten, wenn Sie sich einen Emulator mit einer Reihe von Switch-Blöcken ansehen, insbesondere Switch-Blöcke, die alle über 128 Fälle hatten. Ich hatte den Optimierer auf meinen Funktionen laufen lassen, aber als er zu den Schalterblöcken kam, sagte er "Danke, aber nein, danke, mir geht es gut". Ich hatte nur noch eine Option übrig, und das war, jede Instanz jedes Schalters in eine Funktion zu packen, sie alle in ein Array zu packen und die Recherche selbst durchzuführen. Also habe ich ein Skript geschrieben, um genau das zu tun.

Der ursprüngliche Code würde in etwa so aussehen...

2022-08-07

Es gibt einen Javascript-Emulator für die Rechner TI83+, TI84+ und TI84+CSE namens jsTIified, der von Christopher Mitchell, Gründer der Rechner-Fansite cemetech.net, geschrieben wurde. Es gibt nicht viel Grund, es für etwas anderes zu verwenden, wenn Sie eine native Option zur Verfügung haben, aber wenn Sie dies nicht tun, ist das ziemlich gut. Mein Interesse war geweckt, weil es der erste Emulator war, der den TI84+CSE unterstützte, als dieser Rechner in den frühen 2010er Jahren veröffentlicht wurde. Der CSE war aufregend, weil er einen 320x240-Farbbildschirm auf der Hardwareplattform +SE des 84 nachrüstete, also alle andere Hardware und Betriebssysteme Der Zugriff war bis auf die Grafiken derselbe. Ich wollte ein früher Spieleentwickler für die CSE werden, aber das Entwickeln für den Taschenrechner ohne Emulator oder Debugger ist ziemlich mühsam, also habe ich jsTified mit meinen alten Taschenrechner-ROMs versucht, um ein Gefühl dafür zu bekommen.

jsTified hatte jedoch ein Problem: Es war zu langsam. Diese Rechner verwenden einen z80-Prozessor, der recht einfach zu emulieren ist. Aber jsTified konnte nicht einmal 6-MHz-Rechnermodelle mit voller Geschwindigkeit emulieren, und die CPU des CSE war mit 15 MHz getaktet, also war es noch schlimmer. jsTified ist Closed Source, aber ich habe beschlossen, trotzdem etwas dagegen zu unternehmen.

Das erste, was Sie beim Debuggen einer Webanwendung tun möchten, ist natürlich, auf den Profiler zuzugreifen. Es gab nur einen Hotspot, der alle anderen in den Schatten stellte, und das war der Befehlsdecodierungs- und -ausführungsschalterblock. Das ist so, wie man es erwarten würde, da diese Rechner keine andere komplizierte Hardware wie Pixelverarbeitungseinheiten oder Audiochips zum Emulieren haben, aber es schien ein bisschen zwielichtig zu sein. Ja, Javascript ist langsam, aber Computer, die in den frühen 2000er Jahren hergestellt wurden, hätten die Emulation dieses Taschenrechners mit nativem Code in voller Geschwindigkeit bewältigen können. Javascript-Überlastung war nicht genug, um es zu erklären.

Also fing ich an, mich mit dem eigentlichen Code zu beschäftigen. Ich musste es deminifizieren, aber ich hatte früher mit verschleiertem Minecraft-Code zu tun. Der Befehlsdekodierblock hatte einen riesigen Schalterblock mit zusätzlichen verschachtelten Schalterblöcken für Multibyte-Befehle. In den meisten Sprachen ist das in Ordnung, da Ihr Compiler es in Sprungtabellen umwandelt, also warum habe ich hier keine Sprungtabellenleistung gesehen? Ich war damals aufgrund meiner WebGL-Erfahrungen ein wenig besessen von der Leistung von Javascript und hatte bereits gelernt, dass damals JS-Engines Funktionen nicht über eine bestimmte Größe hinaus optimierten. Mit diesem Wissen teilte ich alle verschachtelten Switch-Anweisungen in ihre eigenen Funktionen auf und bat den übergeordneten Switch, sie aufzurufen, um zu sehen, ob das alles erledigen würde.

Jetzt brauchte ich eine Möglichkeit, meinen Code zu laden. Ich habe schnell einen Webserver auf meinem Computer erstellt, der Anfragen an die Upstream-Website weiterleitet, aber die Anfrage von der Emulations-Engine abfängt und stattdessen meinen modifizierten Code zurückgibt. Ich habe /etc/hosts umgestellt, um die Upstream-Domain auf 127.0.0.1 zu verweisen, und mein Code wurde geladen.

Leider habe ich fast keine Beschleunigung gesehen. Ich war mir zu diesem Zeitpunkt ziemlich sicher, dass ich mich innerhalb der Größenbeschränkungen für die Funktionen befand, also muss etwas anderes fehlen. Ich suchte nach Low-Level-Details zur Implementierung von Switch-Anweisungen in Javascript. Schließlich fand ich einen Stackoverflow-Beitrag von jemandem, der genau dasselbe versuchte wie ich: einen (anderen) z80-Emulator zu optimieren. Da sah ich einen zutiefst beunruhigenden Kommentar mit Quellen, die direkt aus dem V8-Quellcode von Chrome zitiert wurden:

@LGB tatsächlich in V8 (JS-Engine, die von Google Chrome verwendet wird), müssen Sie viele Schritte durchlaufen, um das Schaltergehäuse zu optimieren: Alle Gehäuse müssen vom gleichen Typ sein. Alle Fälle müssen entweder String-Literale oder 31-Bit-Integer-Literale mit Vorzeichen sein. Und es müssen weniger als 128 Fälle sein. Und selbst nach all diesen Reifen erhalten Sie nur das, was Sie mit if-elses sowieso bekommen hätten (dh keine Sprungtabellen oder ähnliches). Wahre Geschichte.

Überprüfen Sie den Beitrag selbst hier https://stackoverflow.com/questions/18830626/should-i-use-big-switch-statements-in-javascript-without-performance-problems#comment27798374_18830724

Nicht das, was Sie hören möchten, wenn Sie sich einen Emulator mit einer Reihe von Switch-Blöcken ansehen, insbesondere Switch-Blöcke, die alle über 128 Fälle hatten. Ich hatte den Optimierer auf meinen Funktionen laufen lassen, aber als er zu den Schalterblöcken kam, sagte er "Danke, aber nein, danke, mir geht es gut". Ich hatte nur noch eine Option übrig, und das war, jede Instanz jedes Schalters in eine Funktion zu packen, sie alle in ein Array zu packen und die Recherche selbst durchzuführen. Also habe ich ein Skript geschrieben, um genau das zu tun.

Der ursprüngliche Code würde in etwa so aussehen...

What's Your Reaction?

like

dislike

love

funny

angry

sad

wow