Einleitung
Hey zusammen. Willkommen im November. Ich hoffe, dir geht es gut.
Wir hatten einen super stressigen Oktober. Wir haben gerade eine große Verbesserung für Community-Lösungen veröffentlicht. Wir deduplizieren sie jetzt, sodass ähnliche Lösungen nur einmal angezeigt werden, wir haben neue Sortieroptionen hinzugefügt, die Möglichkeit, nach Code zu suchen, und wenn du dir C# ansiehst, wirst du sehen, dass wir auch die Möglichkeit hinzugefügt haben, nach verschiedenen Programmierkonzepten zu filtern. Du kannst also nach Lösungen suchen, die Bit-Shifting, Rekursion oder alles andere verwenden, was dir gefällt. Wir werden das in der Community-Woche auf andere Tracks ausrollen.
Aber konzentrieren wir uns zunächst auf #12in23. Der Oktober war ein interessanter Monat, in dem wir objektorientierte Sprachen erkundet haben, aber diesen Monat wird es hardcore. Wir konzentrieren uns auf Assemblersprachen und speziell auf MIPS Assembly, x86-64 Assembly und WebAssembly. Wie gewohnt wird Erik uns erklären, was diese Sprachen interessant und einzigartig macht.
Die Abzeichen
Wie immer kannst du das Nibbly-November-Abzeichen verdienen, indem du 5 beliebige Übungen in diesen Sprachen abschließt. Wir haben auch das Year-Long-Abzeichen, von dem ich weiß, dass viele von euch darauf hinarbeiten. Dafür haben wir 5 ausgewählte Übungen für dich, die du abschließen kannst. Das sind:
- Pop Count: Zähle die 1-Bits in einer Zahl
- Grains: Berechne die Anzahl der Körner auf einem Schachbrett mit sich verdoppelnden Feldern
- Resistor Color: Wandele die Farbe eines Widerstandsbands in ihre numerische Darstellung um
- Rotational Cipher: Implementiere die Rotationschiffre (auch bekannt als Caesar-Chiffre)
- Nucleotide Count: Berechne, wie oft jedes Nukleotid in einem DNA-String vorkommt
Hintergrund
Warum dieser Name Nibble November?
Nun, wie du wahrscheinlich weißt, ist ein Byte 8 Bits. Und ein Nibble ist 4 Bits. Der Name ergibt Sinn, wenn du das y in Byte durch ein i ersetzt, was bite ergibt. Ein Nibble ist dann ein kleiner Bissen.
Was ist eine Assemblersprache?
Nun, fangen wir mit ein paar Grundlagen an. Eine CPU führt Anweisungen aus, wie „addiere zwei Zahlen“ oder „verschiebe Bits nach links“, usw. Diese Anweisungen sind als Maschinencode-Anweisungen bekannt und sie sind nichts weiter als spezifische Bitfolgen. Ein Programm auszuführen ist „einfach“ (in Anführungszeichen) die CPU, die diese Bitfolgen verarbeitet und ausführt.
Da das direkte Aufschreiben von Anweisungen als Bitfolgen umständlich und fehleranfällig ist, entwickelten Kathleen und Andrew Donald Booth bereits 1947 eine menschenfreundlichere Sprache zur Darstellung von Maschinencode-Anweisungen. Eine solche Sprache, die Maschinencode-Anweisungen darstellt, wird als Assemblersprache bezeichnet. Diese Assemblersprache wird dann über einen „Assembler“ in Maschinencode-Anweisungen umgewandelt.
Ein interessanter Aspekt von Assemblersprachen ist, dass sie direkt an eine CPU-Architektur gebunden sind, obwohl sie normalerweise betriebssystemunabhängig sind.
Übrigens, wenn du dich an Lochkarten erinnerst, diese großen Kartenstücke, die in den frühesten Computern verwendet wurden, um Programme auszuführen, sie waren auch eine Assemblersprache!
Wie unterscheidet sich Assemblersprache von den Sprachen, in denen wir heute programmieren?
Der Hauptunterschied ist, dass Assemblersprachen sehr hardwarenah sind. Viele Abstraktionen, an die du dich gewöhnt hast, fehlen. Klassen oder Objekte gibt es nicht. Schleifen? Die musst du manuell über Sprünge schreiben. Funktionen? Fehlanzeige! Ich fand es sehr demütigend, Assembly-Code zu schreiben, weil man dann erkennt, wie sehr moderne Sprachen das Leben erleichtern. Aber es ist auch unglaublich nützlich, Assembly-Code zu schreiben, weil man ein viel besseres Verständnis dafür bekommt, wie die Dinge tatsächlich funktionieren.
Fun Fact: 99 % des Quellcodes von RollerCoaster Tycoon war handgeschriebener Assembly-Code! Das ist eine erstaunliche Leistung, die du noch mehr zu schätzen lernen wirst, wenn du selbst ein wenig Assembly gemacht hast.
Schreiben die Leute noch Assembly-Code?
Nun, weniger als früher. Früher war es so, dass handgeschriebenes Assembly oft Compiler-generierten Maschinencode übertraf (was ein Grund dafür ist, dass die Sprache C++ das direkte Einbetten von Assembly-Code erlaubt), aber Compiler sind so gut darin geworden, Maschinencode zu erzeugen, dass dies heute selten zutrifft. Allerdings findet man Assemblersprache immer noch in leistungskritischen oder ressourcenbeschränkten Umgebungen.
Überblicke
MIPS
- MIPS (Microprocessor without Interlocked Pipelined Stages) ist eine Familie von Befehlssatzarchitekturen für Reduced Instruction Set Computer (RISC).
- Entwickelt von MIPS Computer Systems und erstmals 1985 veröffentlicht
- Mehrere Versionen: MIPS I, II, III, IV, V und MIPS32/64. Die ersten beiden Versionen waren nur 32-Bit, aber MIPS III führte 64-Bit-Unterstützung ein.
- Mehrere optionale Erweiterungen, wie SIMD-Anweisungen und Komprimierung
- Sehr einflussreich für spätere RISC-Architekturen
- MIPS kündigte 2021 an, dass die MIPS-Architektur nicht mehr weiterentwickelt wird und sie zu RISC-V migrierten (einer Open-Source- und lizenzkostenfreien Architektur).
- Hauptsächlich verwendet in eingebetteten Systemen (z. B. Routern) und Servern (Silicon Graphics Computer nutzten es, die berühmt für SFX in Filmen waren), dem NEC Cenju-4 Supercomputer, Teslas Model S Auto, NASAs New Horizons Sonde, aber auch um Assembly an Universitäten zu lehren und in mehreren Spielkonsolen (z. B. original PlayStation, Playstation Portable und Nintendo 64).
x86-64 Assembly
- Entworfen von AMD und 1999 als AMD64-Architektur veröffentlicht
- Es ist eine 64-Bit-Version des x86-Befehlssatzes, der bis 1978 zurückreicht, als Intel seinen 8086-Mikroprozessor auf den Markt brachte. Das war ein 16-Bit-Prozessor, aber später fügte der 80386 32-Bit-Anweisungen hinzu, und dieser Befehlssatz ist synonym mit x86 geworden.
- Die Kernsache, die 64-Bit ermöglichte, war die Adressierung von mehr Speicher (32-Bit-Adressierung ist auf 4 GB begrenzt), was zu einem Engpass geworden war. 64-Bit kann theoretisch 16 Exabytes adressieren, aber derzeit werden nur 48 Bits verwendet, was die Adressierung von 256 TB ermöglicht (kann später bei Bedarf erweitert werden).
- AMD64 erweitert den x86-Befehlssatz und wurde so entworfen, dass es über einen Kompatibilitätsmodus vollständig mit bestehenden 16- und 32-Bit-Anwendungen kompatibel ist
- Intel hatte IA-64 ohne Beteiligung von AMD entworfen. Es war ein neuer, sehr anderer und abwärtsinkompatibler 64-Bit-Befehlssatz. Am Ende setzte sich AMD64 durch und Intel implementierte seine eigene Version davon (mit nur geringfügigen semantischen Unterschieden).
- Überall verwendet. Von Workstations bis Servern (einschließlich Supercomputer), von eingebetteten Systemen bis Spielkonsolen (z. B. PS5 und Xbox Series X).
WebAssembly
- Entworfen vom W3C, einem Standardisierungsgremium für Webtechnologien
- Die Designziele sind:
- Schnell, sicher und portabel
- Effiziente und portable Darstellung
- Früher wurde schnelle Ausführung im Web üblicherweise über spezialisierte Browser-Plugins wie Flash und Silverlight erreicht, da JavaScript selbst sich nicht sehr gut für Hochleistungsrechnen eignet. Die größten Nachteile dieser Plugins waren, dass sie meist viele Sicherheitsprobleme hatten und nicht standardisiert waren.
- Mozilla entwarf asm.js, eine Teilmenge von JavaScript, die das Ausführen von Code im Browser mit großartigen Leistungseigenschaften ermöglichen sollte, was durch Typkonsistenz (keine dynamische Änderung von Typen) und keine Garbage Collection erreicht wurde. Sprachen konnten dann nach asm.js kompilieren und trotzdem gute Leistung im Web erzielen. Es war jedoch immer noch JS, also begrenzt in dem, was erreicht werden konnte. Daher der Vorschlag für eine neue Sprache: WASM.
- Assembly-ähnliche Sprache, insofern sie eine Reihe auszuführender Anweisungen bereitstellt. Entscheidend ist, dass sie nicht an eine bestimmte CPU gebunden ist, also plattformunabhängig ist und eine Implementierung für jede Plattform erfordert (eine virtuelle Maschine). Das bedeutet, dass WebAssembly tatsächlich Bytecode ist, kein Maschinencode.
- Statisch typisiert (entscheidender Unterschied zu JS)
- Verwendet normalerweise Ahead-of-Time- oder Just-in-Time-Kompilierung (kann aber interpretiert werden)
- Offener Standard und definiert zwei Dinge:
- Ein Binärformat
- Ein Textformat (das in das Binärformat kompiliert wird)
- Implementierungen in allen großen Browsern
- Wird in vielen Webseiten verwendet, die hohe Leistung erfordern, wie Google Earth, Figma, Unity und Autocad. Hat auch serverseitig an Bedeutung gewonnen, z. B. um Microservices auszuführen, auf SaaS-Plattformen (z. B. CloudFlare Workers) oder in Docker zu laufen.
Und aus Programmierperspektive, wie unterscheiden sie sich?
MIPS
- Verwendet eine Load-Store-Architektur (auch bekannt als Register-Register), bei der Anweisungen entweder Speicherzugriffe oder Arithmetik ausführen, aber ausschließlich mit Daten in Registern arbeiten
x86-64 Assembly
- Verwendet eine Register-Speicher-Architektur, die Operationen auf (oder von) Speicher sowie auf Registern ermöglicht.
WebAssembly
- Verwendet stackbasierte Programmierung (keine Register) mit der Option, Daten aus dem Speicher zu lesen und in den Speicher zu schreiben
Was macht diese Sprachen großartig?
MIPS
- Klein. Der MIPS-Befehlssatz passt mit all seinen Anweisungen auf eine einzige Seite.
- Etablierte Aufrufkonventionen helfen zu erkennen, wie die verfügbaren Register zu verwenden sind, z. B. welche zum Übergeben von Argumenten und welche zum Zurückgeben von Ergebnissen dienen.
- Stabil. Die letzte Version wurde 2014 veröffentlicht.
- Umfassend dokumentiert, insbesondere in akademischen Texten.
- Viele reale Anwendungen. Milliarden von Geräten.
x86-64 Assembly
- Obwohl eine Erweiterung von x64, wurden viele neue Funktionen hinzugefügt, darunter:
- 64-Bit-Ganzzahlunterstützung
- Zusätzliche Register
- SSE-Anweisungen (Vektoranweisungen)
- Relativer Datenzugriff (effizienter bei der Verwendung von Shared Libraries)
- No-Execute-Bit (Sicherheitsfunktion, um die Ausführung von Code in bestimmten Speicherseiten zu verhindern)
- Vertraut. Da es den x86-Befehlssatz erweitert, wird es für diejenigen, die mit dem x86-Befehlssatz vertraut sind, relativ einfach zu lernen sein. Umfangreiche und detaillierte Dokumentation
- Stabil. Während regelmäßig neue Versionen hinzugefügt werden, bleibt der Kern extrem stabil und abwärtskompatibel.
WebAssembly
- Die stackbasierte WebAssembly-Virtual Machine ist schlank und einfach im Vergleich zu den Assemblersprachen physischer Prozessoren (einschließlich RISC). Das macht das Kompilieren dafür relativ einfach.
- Das WebAssembly-Textformat verwendet S-Expression-„Zucker“, um einen vertrauten imperativen Stil zu erreichen, der in stackbasierten Code umgewandelt wird. Die Form mit S-Expressions wird als „sugared form“ bezeichnet und wird zu der anderen Form „desugart“, die dem entspricht, was im Binärformat steht. S-Expressions sind jedem vertraut, der schon einmal mit einem LISP gearbeitet hat.
- Starke Interoperabilität mit JavaScript. Einfacher Datenaustausch von und zu JavaScript. Wichtiger Vorbehalt: WASM erlaubt (noch) keine Interaktion mit dem DOM.
- Wird kontinuierlich verbessert. Nicht nur die WASM-Virtual Machines werden verbessert, auch der Standard selbst wird aktiv weiterentwickelt. Unzählige neue Funktionen werden entworfen und bearbeitet, wie SIMD-bezogene Anweisungen, Garbage Collection, Threads, Tail-Call-Optimierungen und mehr.
- Sicher. Code wird validiert und in einer Sandbox-Umgebung ausgeführt, was einen höheren Grad an statischer Validierung im Vergleich zu JavaScript oder nativen Assemblersprachen bietet. Die Semantik ist wohldefiniert, was sie leicht zu prüfen und zu verstehen macht.
Herausragende Merkmale
MIPS
- Effizienz. MIPS-Prozessoren sind sehr effizient, was sie ideal für eingebettete Systeme macht.
- Leistung. Große Leistung, daher wurde MIPS in Supercomputern eingesetzt.
- Leicht zu lernen. Mit wenigen Anweisungen und jeder Anweisung, die nur eine einfache Sache tut, ist es leicht zu lernen. Ideal für Lehrzwecke.
x86-64 Assembly
- Leistungsstark. x86-64 wurde über Jahrzehnte optimiert, mit unzähligen Anweisungen, die die Leistung verbessern. Ein Beispiel dafür ist SIMD (Single Instruction, Multiple Data), das sind Anweisungen, die, nun ja, eine Anweisung parallel auf mehreren Daten ausführen können.
- Allgegenwärtig. Geräte, die x86-64 ausführen, gibt es überall. Intel- und AMD-CPUs implementieren es. Es ist seit geraumer Zeit der De-facto-Standard.
- Regelmäßig aktualisiert. Z. B. neue Vektoranweisungen, mit SSE3-5, AVX, AVX-512 und mehr.
WebAssembly
- Effizient. Das Binärformat ist kompakt und kann in einem schnellen Durchgang dekodiert, validiert und kompiliert werden. Es ist auch streamfähig, was ermöglicht, mit der Dekodierung, Validierung und Kompilierung so früh wie möglich zu beginnen, bevor alle Daten gesehen wurden. Und es ist auch parallelisierbar. Das macht es perfekt für Hochleistungs-Webanwendungen.
- Großartiges Kompilierungsziel. Ermöglicht Code in vielen Sprachen, im Web zu laufen. Die meisten großen Sprachen unterstützen das Kompilieren zu WebAssembly-Binärdateien, was bedeutet, dass dein Code im Web laufen kann, ohne JavaScript schreiben zu müssen. Einige Sprachen kompilieren deinen Code nicht zu WebAssembly, sondern kompilieren die Laufzeitumgebung zu WebAssembly, die dann unveränderten Bytecode ausführen kann.
- Einfach bereitzustellen und auszuführen. Alles, was du brauchst, ist eine virtuelle Maschine, die den Bytecode interpretieren kann, und alle großen Browser enthalten eine.
- Nicht an das Web gebunden, kann aber auch serverseitig ausgeführt werden. Das WebAssembly System Interface (WASI) ist eine Schnittstelle (ABI und API), die so entworfen wurde, dass sie auf jede Plattform portierbar ist. Es ähnelt POSIX (Standard-Schnittstellen für Unix-Systeme) und bietet Dinge wie E/A. Sicherheit ist ein Schlüsselelement seines Designs, das Sandboxing und Capability-Orientierung umfasst (du musst explizit Berechtigungen für Dinge wie Dateien oder Sockets anfordern). WASI hat auch das Potenzial, eine einfache Schnittstelle zwischen Sprachen zu ermöglichen. Solomon Hykes, Mitbegründer von Docker, schrieb 2019: „Wenn WASM+WASI 2008 existiert hätte, hätten wir Docker nicht erstellen müssen.“
Welche wählen
- Wenn du noch nie mit einer Assemblersprache gearbeitet hast, ist WebAssembly wahrscheinlich die am einfachsten zu erlernende Sprache. Allerdings, wenn du eine Assemblersprache lernen möchtest, die zu Maschinencode kompiliert, probiere MIPS Assembly aus.
- Wenn du auf einer x86-64-Maschine arbeitest (sehr wahrscheinlich), probiere x86-64 Assembly aus.
- Wenn du mit LISP vertraut bist, wirst du zu schätzen wissen, dass WebAssembly S-Expressions verwendet.
- Wenn du mit Web-Apps arbeitest, ist WebAssembly die logischste Option.
- Wenn dir Leistung wichtig ist, sind x86-64 und MIPS großartige Optionen. Oder wenn dir Web-Performance wichtig ist, probiere WebAssembly.