Basierend auf meiner Erfahrung mit range-set-blaze
ein Datenstrukturprojekt, hier sind die von mir empfohlenen Entscheidungen, einzeln beschrieben. Um Wischiwaschi zu vermeiden, formuliere ich sie als Regeln.
Im Jahr 2019 warfare Docker-Mitschöpfer Solomon Hykes getwittert:
Wenn es WASM+WASI im Jahr 2008 gegeben hätte, hätten wir Docker nicht erstellen müssen. So wichtig ist es. Webassembly auf dem Server ist die Zukunft der Datenverarbeitung. Eine standardisierte Systemschnittstelle warfare das fehlende Bindeglied. Hoffen wir, dass WASI dieser Aufgabe gewachsen ist.
Wenn Sie heute Technologienachrichten verfolgen, werden Sie optimistische Schlagzeilen wie diese sehen:
Wenn WASM WASI wirklich bereit und nützlich wäre, würde es bereits jeder nutzen. Die Tatsache, dass wir diese Schlagzeilen immer wieder sehen, deutet darauf hin, dass es noch nicht fertig ist. Mit anderen Worten: Sie müssten nicht ständig darauf bestehen, dass WASM WASI bereit ist, wenn es wirklich so wäre.
Ab WASI Preview 1 ist der Stand der Dinge wie folgt: Sie können auf einige Dateioperationen und Umgebungsvariablen zugreifen und haben Zugriff auf die Zeit- und Zufallszahlengenerierung. Es gibt jedoch keine Unterstützung für die Vernetzung.
WASM WASI könnte für bestimmte Webdienste im AWS-Lambda-Stil nützlich sein, aber selbst das ist ungewiss. Denn würden Sie es nicht vorziehen, Ihren Rust-Code nativ zu kompilieren und im Vergleich zu WASM WASI doppelt so schnell und zur Hälfte der Kosten auszuführen?
Vielleicht ist WASM WASI für Plug-Ins und Erweiterungen nützlich. In der Genomik habe ich eine Rust-Erweiterung für Python, die ich für 25 verschiedene Kombinationen kompiliere (5 Versionen von Python auf 5 Betriebssystemzielen). Trotzdem decke ich nicht alle möglichen Betriebssysteme und Chipfamilien ab. Könnte ich diese Betriebssystemziele durch WASM WASI ersetzen? Nein, es wäre zu langsam. Könnte ich WASM WASI als sechstes „Catch-All“-Ziel hinzufügen? Vielleicht, aber wenn ich wirklich Portabilität benötige, muss ich Python bereits unterstützen und sollte einfach Python verwenden.
Wofür ist WASM WASI additionally intestine? Derzeit liegt sein Hauptwert darin, dass er einen Schritt hin zur Ausführung von Code im Browser oder auf eingebetteten Systemen darstellt.
In Regel 1 habe ich nebenbei „Betriebssystemziele“ erwähnt. Schauen wir uns die Rust-Ziele genauer an – wichtige Informationen nicht nur für WASM WASI, sondern auch für die allgemeine Rust-Entwicklung.
Auf meinem Home windows-Rechner kann ich ein Rust-Projekt kompilieren, um es unter Linux oder macOS auszuführen. Ebenso kann ich von einem Linux-Rechner aus ein Rust-Projekt für Home windows oder macOS kompilieren. Hier sind die Befehle, die ich verwende, um das Linux-Ziel zu einem Home windows-Pc hinzuzufügen und zu überprüfen:
rustup goal add x86_64-unknown-linux-gnu
cargo test --target x86_64-unknown-linux-gnu
Nebenbei: Während
cargo test
Überprüft, ob der Code kompiliert wird. Für die Erstellung einer voll funktionsfähigen ausführbaren Datei sind zusätzliche Instruments erforderlich. Für die Crosskompilierung von Home windows zu Linux (GNU) müssen Sie außerdem den Linux GNU C/C++-Compiler und die entsprechende Toolchain installieren. Das kann schwierig sein. Glücklicherweise ist die erforderliche Toolchain für die WASM-Ziele, die uns wichtig sind, einfach zu installieren.
Um alle von Rust unterstützten Ziele anzuzeigen, verwenden Sie den folgenden Befehl:
rustc --print target-list
Es werden über 200 Ziele aufgelistet, darunter x86_64-unknown-linux-gnu
, wasm32-wasip1
Und wasm32-unknown-unknown
.
Zielnamen bestehen aus bis zu vier Teilen: CPU-Familie, Hersteller, Betriebssystem und Umgebung (z. B. GNU vs. LVMM):
Nachdem wir nun etwas über Ziele verstanden haben, können wir mit der Set up des Ziels fortfahren, das wir für WASM WASI benötigen.
Um unseren Rust-Code auf WASM außerhalb eines Browsers auszuführen, müssen wir ihn gezielt ausführen wasm32-wasip1
(32-Bit-WebAssembly mit WASI-Vorschau 1). Wir werden auch WASMTIME installieren, eine Laufzeit, die es uns ermöglicht, WebAssembly-Module mithilfe von WASI außerhalb des Browsers auszuführen.
rustup goal add wasm32-wasip1
cargo set up wasmtime-cli
Um unser Setup zu testen, erstellen wir eine neue „Hey, WebAssembly!“ Rust-Projekt mit cargo new
. Dadurch wird ein neues Rust-Paket initialisiert:
cargo new hello_wasi
cd hello_wasi
Bearbeiten src/principal.rs
zu lesen:
fn principal() {
#(cfg(not(target_arch = "wasm32")))
println!("Hey, world!");
#(cfg(target_arch = "wasm32"))
println!("Hey, WebAssembly!");
}
Nebenbei: Wir werden uns das genauer ansehen
#(cfg(...))
Attribut, das die bedingte Kompilierung ermöglicht, in Regel 4.
Führen Sie nun das Projekt mit aus cargo run
und Sie sollten sehen Hey, world!
auf der Konsole gedruckt.
Als nächstes erstellen Sie eine .cargo/config.toml
Datei, die angibt, wie Rust das Projekt ausführen und testen soll, wenn es auf WASM WASI abzielt.
(goal.wasm32-wasip1)
runner = "wasmtime run --dir ."
Nebenbei: Dies
.cargo/config.toml
Die Datei unterscheidet sich von der HauptdateiCargo.toml
Datei, die die Abhängigkeiten und Metadaten Ihres Projekts definiert.
Wenn Sie nun sagen:
cargo run --target wasm32-wasip1
Das solltest du sehen Hey, WebAssembly!
. Glückwunsch! Sie haben gerade erfolgreich Rust-Code in der Container-ähnlichen WASM WASI-Umgebung ausgeführt.
Lassen Sie uns nun der Sache nachgehen #(cfg(...))
– ein unverzichtbares Werkzeug zum bedingten Kompilieren von Code in Rust. In Regel 3 sahen wir:
fn principal() {
#(cfg(not(target_arch = "wasm32")))
println!("Hey, world!");
#(cfg(target_arch = "wasm32"))
println!("Hey, WebAssembly!");
}
Der #(cfg(...))
Zeilen weisen den Rust-Compiler an, bestimmte Codeelemente basierend auf bestimmten Bedingungen einzuschließen oder auszuschließen. Ein „Codeelement“ bezieht sich auf eine Codeeinheit wie eine Funktion, Anweisung oder einen Ausdruck.
Mit #(cfg(…))
Zeilen können Sie Ihren Code bedingt kompilieren. Mit anderen Worten: Sie können unterschiedliche Versionen Ihres Codes für unterschiedliche Situationen erstellen. Zum Beispiel beim Kompilieren für die wasm32
Ziel, der Compiler ignoriert das #(cfg(not(target_arch = "wasm32")))
Block und umfasst nur Folgendes:
fn principal() {
println!("Hey, WebAssembly!");
}
Sie geben Bedingungen über Ausdrücke an, z. B. target_arch = "wasm32"
. Zu den unterstützten Schlüsseln gehören: target_os
Und target_arch
. Siehe die Rust-Referenz für die vollständige Liste der unterstützten Schlüssel. Sie können Ausdrücke auch mit Cargo-Funktionen erstellen, worüber wir in Regel 6 erfahren.
Sie können Ausdrücke mit den logischen Operatoren kombinieren not
, any
Und all
. Die bedingte Kompilierung von Rust verwendet keine traditionelle Kompilierung if...then...else
Aussagen. Stattdessen müssen Sie verwenden #(cfg(...))
und seine Negation, um verschiedene Fälle zu behandeln:
#(cfg(not(target_arch = "wasm32")))
...
#(cfg(target_arch = "wasm32"))
...
Um eine ganze Datei bedingt zu kompilieren, platzieren Sie #!(cfg(...))
oben in der Datei. (Beachten Sie das „!“). Dies ist nützlich, wenn eine Datei nur für ein bestimmtes Ziel oder eine bestimmte Konfiguration related ist.
Sie können auch verwenden cfg
Ausdrücke in Cargo.toml
Abhängigkeiten bedingt einzubeziehen. Dadurch können Sie Abhängigkeiten an verschiedene Ziele anpassen. Hier heißt es zum Beispiel: „Verlassen Sie sich auf Criterion mit Rayon, wenn Sie nicht zielen.“ wasm32
“.
(goal.'cfg(not(target_arch = "wasm32"))'.dev-dependencies)
criterion = { model = "0.5.1", options = ("rayon") }
Nebenbei: Weitere Informationen zur Verwendung
cfg
Ausdrücke inCargo.toml
siehe meinen Artikel: Neun Rust Cargo.toml Wats und Wat Nots: Beherrschen Sie die Formatierungsregeln von Cargo.toml und vermeiden Sie Frustration | Auf dem Weg zur Datenwissenschaft (medium.com).
Es ist Zeit zu versuchen zu rennen dein Projekt zu WASM WASI. Erstellen Sie wie in Regel 3 beschrieben eine .cargo/config.toml
Datei für Ihr Projekt. Es erklärt Cargo, wie es Ihr Projekt auf WASM WASI ausführen und testen soll.
(goal.wasm32-wasip1)
runner = "wasmtime run --dir ."
Nächste, Ihr Projekt sollte – wie jeder gute Code – bereits Checks enthalten. Mein range-set-blaze
Das Projekt umfasst beispielsweise diesen Take a look at:
#(take a look at)
fn insert_255u8() {
let range_set_blaze = RangeSetBlaze::<u8>::from_iter((255));
assert!(range_set_blaze.to_string() == "255..=255");
}
Versuchen wir nun, die Checks Ihres Projekts auf WASM WASI auszuführen. Verwenden Sie den folgenden Befehl:
cargo take a look at --target wasm32-wasip1
Wenn das funktioniert, sind Sie vielleicht fertig – aber es wird wahrscheinlich nicht funktionieren. Wenn ich das anprobiere range-set-blaze
erhalte ich diese Fehlermeldung, die sich über die Verwendung von Rayon auf WASM beschwert.
error: Rayon can't be used when focusing on wasi32. Strive disabling default options.
--> C:Userscarlk.cargoregistrysrcindex.crates.io-6f17d22bba15001fcriterion-0.5.1srclib.rs:31:1
|
31 | compile_error!("Rayon can't be used when focusing on wasi32. Strive disabling default options.");
Um diesen Fehler zu beheben, müssen wir zunächst die Cargo-Funktionen verstehen.
Um Probleme wie den Rayon-Fehler in Regel 5 zu beheben, ist es wichtig zu verstehen, wie die Cargo-Funktionen funktionieren.
In Cargo.toml
ein optionales (options)
Im Abschnitt können Sie verschiedene Konfigurationen oder Versionen Ihres Projekts definieren, je nachdem, welche Funktionen aktiviert oder deaktiviert sind. Hier ist zum Beispiel ein vereinfachter Teil davon Cargo.toml
Datei aus der Kriterien-Benchmarking-Projekt:
(options)
default = ("rayon", "plotters", "cargo_bench_support")
rayon = ("dep:rayon")
plotters = ("dep:plotters")
html_reports = ()
cargo_bench_support = ()(dependencies)
#...
# Elective dependencies
rayon = { model = "1.3", elective = true }
plotters = { model = "^0.3.1", elective = true, default-features = false, options = (
"svg_backend",
"area_series",
"line_series",
) }
Dies definiert vier Cargo-Funktionen: rayon
, plotters
, html_reports
Und cargo_bench_support
. Da jede Funktion ein- oder ausgeschlossen werden kann, ergeben diese vier Funktionen 16 mögliche Konfigurationen des Projekts. Beachten Sie auch die spezielle Commonplace-Cargo-Funktion.
Eine Cargo-Funktion kann andere Cargo-Funktionen umfassen. Im Beispiel das Besondere default
Die Cargo-Funktion umfasst drei weitere Cargo-Funktionen: rayon
, plotters
Und cargo_bench_support
.
Eine Cargo-Funktion kann eine Abhängigkeit enthalten. Der rayon
Die obige Frachtfunktion umfasst die rayon
Kiste als abhängiges Paket.
Darüber hinaus können abhängige Pakete über eigene Cargo-Funktionen verfügen. Zum Beispiel die plotters
Die obige Frachtfunktion umfasst die plotters
abhängiges Paket mit den folgenden aktivierten Cargo-Funktionen: svg_backend
, area_series
Und line_series
.
Sie können angeben, welche Cargo-Funktionen beim Ausführen aktiviert oder deaktiviert werden sollen cargo test
, cargo construct
, cargo run
oder cargo take a look at
. Wenn Sie beispielsweise am Criterion-Projekt arbeiten und nur Folgendes überprüfen möchten html_reports
Funktion ohne Standardeinstellungen können Sie Folgendes ausführen:
cargo test --no-default-features --features html_reports
Dieser Befehl weist Cargo an, standardmäßig keine Cargo-Funktionen einzuschließen, sondern diese gezielt zu aktivieren html_reports
Cargo-Funktion.
In Ihrem Rust-Code können Sie Codeelemente basierend auf aktivierten Cargo-Funktionen einschließen/ausschließen. Die Syntax verwendet #cfg(…)
gemäß Regel 4:
#(cfg(characteristic = "html_reports"))
SOME_CODE_ITEM
Mit diesem Verständnis der Cargo-Funktionen können wir nun versuchen, das Downside zu beheben Rayon
Fehler, der beim Ausführen von Checks auf WASM WASI aufgetreten ist.
Als wir versuchten zu laufen cargo take a look at --target wasm32-wasip1
Teil der Fehlermeldung lautete: Criterion ... Rayon can't be used when focusing on wasi32. Strive disabling default options.
Dies legt nahe, dass wir Criterion deaktivieren sollten rayon
Frachtfunktion beim Anvisieren von WASM WASI.
Dazu müssen wir zwei Änderungen in unserem vornehmen Cargo.toml
. Zuerst müssen wir das deaktivieren rayon
Funktion von Criterion in der (dev-dependencies)
Abschnitt. Additionally diese Startkonfiguration:
(dev-dependencies)
criterion = { model = "0.5.1", options = ("html_reports") }
wird dazu, dass wir die Standardfunktionen für Criterion explizit deaktivieren und dann alle Cargo-Funktionen außer aktivieren rayon
.
(dev-dependencies)
criterion = { model = "0.5.1", options = (
"html_reports",
"plotters",
"cargo_bench_support"),
default-features = false }
Als nächstes, um sicherzustellen rayon
weiterhin für Nicht-WASM-Ziele verwendet wird, fügen wir es mit einer bedingten Abhängigkeit wieder hinzu Cargo.toml
wie folgt:
(goal.'cfg(not(target_arch = "wasm32"))'.dev-dependencies)
criterion = { model = "0.5.1", options = ("rayon") }
Wenn Sie auf WASM WASI abzielen, müssen Sie im Allgemeinen möglicherweise Ihre Abhängigkeiten und ihre Cargo-Funktionen ändern, um die Kompatibilität sicherzustellen. Manchmal ist dieser Prozess unkompliziert, aber manchmal kann er eine Herausforderung sein – oder sogar unmöglich, wie wir in Regel 8 besprechen werden.
Nebenbei: Im nächsten Artikel dieser Reihe – über WASM im Browser – werden wir tiefer auf Strategien zur Behebung von Abhängigkeiten eingehen.
Nachdem wir die Checks erneut ausgeführt haben, überwinden wir den vorherigen Fehler, nur um dann auf einen neuen zu stoßen, was ein Fortschritt ist!
#(take a look at)
fn test_demo_i32_len() {
assert_eq!(demo_i32_len(i32::MIN..=i32::MAX), u32::MAX as usize + 1);
^^^^^^^^^^^^^^^^^^^^^ try to compute
`usize::MAX + 1_usize`, which might overflow
}
Der Compiler beschwert sich darüber u32::MAX as usize + 1
Überläufe. Unter 64-Bit-Home windows läuft der Ausdruck nicht über, weil usize
ist das gleiche wie u64
und halten kann u32::MAX as usize + 1
. WASM ist jedoch eine 32-Bit-Umgebung usize
ist das gleiche wie u32
und der Ausdruck ist eins zu groß.
Die Lösung hier ist ersetzen usize
mit u64
um sicherzustellen, dass der Ausdruck nicht überläuft. Im Allgemeinen erkennt der Compiler diese Probleme nicht immer, daher ist es wichtig, dass Sie Ihre Verwendung überprüfen usize
Und isize
. Wenn Sie sich auf die Größe oder den Index einer Rust-Datenstruktur beziehen, usize
ist richtig. Wenn Sie es jedoch mit Werten zu tun haben, die die 32-Bit-Grenzen überschreiten, sollten Sie verwenden u64
oder i64
.
Nebenbei: In einer 32-Bit-Umgebung ein Rust-Array,
Vec
,BTreeSet
usw. können nur bis zu 2³²−1=4.294.967.295 Elemente enthalten.
Wir haben additionally das Abhängigkeitsproblem behoben und a usize
Überlauf. Aber können wir alles reparieren? Leider lautet die Antwort nein.
WASM WASI Preview 1 (die aktuelle Model) unterstützt den Dateizugriff (innerhalb eines angegebenen Verzeichnisses), das Lesen von Umgebungsvariablen und das Arbeiten mit Zeit und Zufallszahlen. Allerdings sind seine Fähigkeiten im Vergleich zu dem, was Sie von einem vollständigen Betriebssystem erwarten würden, begrenzt.
Wenn Ihr Projekt Zugriff auf Netzwerke, asynchrone Aufgaben mit Tokio oder Multithreading mit Rayon erfordert, werden diese Funktionen in Vorschau 1 leider nicht unterstützt.
Glücklicherweise wird erwartet, dass WASM WASI Preview 2 diese Einschränkungen überwindet und mehr Funktionen bietet, einschließlich einer besseren Unterstützung für Netzwerk- und möglicherweise asynchrone Aufgaben.
So bestehen Ihre Checks auf WASM WASI und Ihr Projekt läuft erfolgreich. Bist du fertig? Nicht ganz. Denn wie ich gerne sage:
Wenn es nicht in CI ist, existiert es nicht.
Steady Integration (CI) ist ein System, das Ihre Checks jedes Mal automatisch ausführen kann, wenn Sie Ihren Code aktualisieren, um sicherzustellen, dass Ihr Code weiterhin wie erwartet funktioniert. Durch das Hinzufügen von WASM WASI zu Ihrem CI können Sie garantieren, dass zukünftige Änderungen die Kompatibilität Ihres Projekts mit dem WASM WASI-Ziel nicht beeinträchtigen.
In meinem Fall wird mein Projekt auf GitHub gehostet und ich verwende GitHub Actions als mein CI-System. Hier ist die Konfiguration, die ich hinzugefügt habe .github/workflows/ci.yml
um mein Projekt auf WASM WASI zu testen:
test_wasip1:
identify: Take a look at WASI P1
runs-on: ubuntu-latest
steps:
- identify: Checkout
makes use of: actions/checkout@v4
- identify: Arrange Rust
makes use of: dtolnay/rust-toolchain@grasp
with:
toolchain: secure
targets: wasm32-wasip1
- identify: Set up Wasmtime
run: |
curl https://wasmtime.dev/set up.sh -sSf | bash
echo "${HOME}/.wasmtime/bin" >> $GITHUB_PATH
- identify: Run WASI checks
run: cargo take a look at --verbose --target wasm32-wasip1
Durch die Integration von WASM WASI in CI kann ich sicher neuen Code zu meinem Projekt hinzufügen. CI testet automatisch, ob mein gesamter Code auch in Zukunft WASM WASI unterstützt.