Design: Vorschlag: Sprachübergreifende Typbindungen hinzufügen

Erstellt am 1. Mai 2019  ·  61Kommentare  ·  Quelle: WebAssembly/design

WebAssembly ist derzeit sehr gut darin, Code auszuführen, der in beliebigen Sprachen von einem bestimmten (normalerweise JS) Interpreter geschrieben wurde, aber es fehlen einige wichtige Funktionen, wenn es darum geht , mehrere beliebige Sprachen miteinander zu

Eines dieser Merkmale ist ein sprachunabhängiges Typsystem. Ich möchte vorschlagen, dass WebAssembly eines oder mehrere solcher Systeme hinzugefügt wird.

Abgesehen davon haben einige Mitwirkende in früheren Feature-Diskussionen zum Ausdruck gebracht, dass die Sprachinteroperabilität kein Designziel von WebAssembly sein sollte. Ich stimme zu, dass es nicht unbedingt ein Ziel mit hoher Priorität sein sollte , aber ich denke, es ist ein Ziel, das langfristig angestrebt wird. Bevor ich mich also mit den Designzielen befasse, werde ich die Gründe darlegen, warum ich denke, dass Sprachinteroperabilität den Aufwand wert ist.

Warum sich um die Sprachinteroperabilität kümmern?

Zu den Vorteilen geringerer Sprachbarrieren gehören:

  • Mehr Bibliotheken für wasm-Benutzer : Dies versteht sich von selbst, aber die Verbesserung der Sprachinteroperabilität bedeutet, dass Benutzer vorhandene Bibliotheken häufiger verwenden können, selbst wenn die Bibliothek in einer anderen Sprache geschrieben ist als sie verwenden.

  • Einfachere Einführung kleiner Sprachen: Auf dem aktuellen Markt ist es für Sprachen ohne Unternehmensunterstützung oft schwierig, sich durchzusetzen. Neue Sprachen (und sogar Sprachen wie D mit jahrelanger Verfeinerung) müssen mit Sprachen mit großen Ökosystemen konkurrieren und leiden unter ihrem eigenen Mangel an Bibliotheken. Die Sprachinteroperabilität würde es ihnen ermöglichen, bestehende Ökosysteme wie Pythons oder Javas zu nutzen.

  • Bessere sprachunabhängige Toolchains : Im Moment haben die meisten Sprachen ihr eigenes Schema zum Laden von Bibliotheken und einen eigenen Paketmanager (oder, im Fall von C/C++, mehrere nicht-offizielle). Das Schreiben eines sprachunabhängigen Projekt-Builders ist schwierig, da diese Sprachen oft subtile Abhängigkeiten und ABI-Inkompatibilitäten aufweisen, die eine monolithische projektweite Lösung erfordern, um sie aufzulösen. Ein robustes intersprachliches Typsystem würde es einfacher machen, Projekte in kleinere Module aufzuteilen, die von einer npm-ähnlichen Lösung gehandhabt werden können.

Insgesamt denke ich, dass der erste Punkt mit großem Abstand der wichtigste ist. Ein besseres Typsystem bedeutet einen besseren Zugang zu anderen Sprachen, was mehr Möglichkeiten zur Wiederverwendung von Code bedeutet, anstatt ihn von Grund auf neu zu schreiben. Ich kann gar nicht genug betonen, wie wichtig das ist.

Anforderungen

Vor diesem Hintergrund möchte ich die Anforderungen skizzieren, die ein sprachübergreifendes Typsystem erfüllen muss.

Ich schreibe unter der Annahme, dass das Typsystem ausschließlich verwendet wird, um zwischen Modulen übergebene Funktionen mit Anmerkungen zu versehen, und nicht überprüfen würde, wie Sprachen ihren eigenen linearen oder verwalteten Speicher in irgendeiner Weise verwenden.

Um in einer Wasm-Umgebung wirklich nützlich zu sein, würde ein solches Typensystem Folgendes benötigen:

1 - Sicherheit

  • Typsicher: Der Angerufene darf nur Zugriff auf die vom Anrufer angegebenen Daten haben, im Stil von Objektfähigkeiten.

    • Die Erinnerung sollte am Ende eines Anrufs "vergessen" werden. Ein Angerufener sollte nicht in der Lage sein, auf die Daten eines Anrufers zuzugreifen, zurückzukehren und dann in irgendeiner Form wieder auf diese Daten zuzugreifen.

2 - Gemeinkosten

  • Entwickler sollten damit vertraut sein, regelmäßig modulübergreifende Aufrufe durchzuführen, z. B. in einer Renderschleife.

    • Zero-Copy: Das Typsystem sollte ausdrucksstark genug sein, um es Interpreten zu ermöglichen, Zero-Copy-Strategien zu implementieren, wenn sie wollen, und ausdrucksstark genug, damit diese Implementierer wissen, wann Zero-Copy optimal ist.

3 - Strukturdiagramme

  • Das Typsystem sollte Strukturen, optionale Zeiger, Arrays variabler Länge, Slices usw. enthalten.

    • Im Idealfall sollte der Aufrufer in der Lage sein, ein im Speicher verstreutes Objektdiagramm zu senden, während die Anforderungen 1 und 2 eingehalten werden.

4 - Referenztypen

  • Module sollten in der Lage sein, tief in Strukturgraphen verschachtelte Referenztypen auszutauschen.

5 - Brücke zwischen Speicherlayouts

  • Dies ist ein sehr wichtiger Punkt. Verschiedene Kategorien von Sprachen haben unterschiedliche Anforderungen. Sprachen, die auf linearen Speicher angewiesen sind, möchten Speicherabschnitte übergeben, während Sprachen, die auf GC angewiesen sind, GC-Referenzen übergeben möchten.

    • Ein ideales Typsystem sollte semantische Typen ausdrücken und Sprachen entscheiden lassen, wie sie im Gedächtnis interpretiert werden. Während die Weitergabe von Daten zwischen Sprachen mit inkompatiblen Speicherlayouts immer mit einem gewissen Overhead verbunden ist, sollte die Weitergabe von Daten zwischen ähnlichen Sprachen idealerweise kostengünstig sein (zB sollten Einbetter Serialisierungs-Deserialisierungsschritte vermeiden, wenn ein Memcpy die gleiche Aufgabe erfüllen kann).

    • Zusätzliche Bindungen können auch Caching und andere Optimierungsstrategien ermöglichen.

    • Die Konvertierungsarbeit beim Übergeben von Daten zwischen zwei Modulen sollte für den Entwickler transparent sein, solange die semantischen Typen kompatibel sind.

6 - Fehlerbehandlung während der Kompilierung

  • Jeder Fehler im Zusammenhang mit ungültigen Funktionsaufrufargumenten sollte zur Kompilierzeit erkennbar und ausdrückbar sein, anders als zB in JS, wo TypeErrors zur Laufzeit geworfen werden, wenn versucht wird, das Argument auszuwerten.
  • Im Idealfall sollten Sprachcompiler selbst Typfehler beim Importieren von wasm-Modulen erkennen und ausdrucksstarke, idiomatische Fehler an den Benutzer ausgeben. In welcher Form diese Fehlerprüfung erfolgen soll, müsste im Repository tool-conventions detailliert beschrieben werden.
  • Dies bedeutet, dass eine IDL mit vorhandenen Konvertern in andere Sprachen von Vorteil wäre.

7 - Geben Sie einen Schelling-Punkt für die Interaktion zwischen den Sprachen an

  • Dies ist leichter gesagt als getan, aber ich denke, wasm sollte allen Compiler-Autoren ein Signal senden, dass die Standardmethode für die Interoperabilität zwischen Sprachen X ist. Aus offensichtlichen Gründen ist es nicht wünschenswert, mehrere konkurrierende Standards für die Sprachinteroperabilität zu haben.

Vorgeschlagene Implementierung

Ich schlage vor, Bindungen an die Cap'n'Proto-IDL von @kentonv zu Webassembly hinzuzufügen.

Sie würden ähnlich wie WebIDL-Bindungen funktionieren: wasm-Module würden Funktionen exportieren und spezielle Anweisungen verwenden, um sie an typisierte Signaturen zu binden; andere Module würden diese Signaturen importieren und an ihre eigenen Funktionen binden.

Die folgende Pseudo-Syntax soll eine Vorstellung davon geben, wie diese Bindungen aussehen würden; es ist approximativ und stark vom WebIDL-Vorschlag inspiriert und konzentriert sich mehr auf die technischen Herausforderungen als auf die Bereitstellung erschöpfender Listen von Anweisungen.

Capnproto-Bindungsanweisungen würden alle in einem neuen Cap'n'Proto-Bindungsabschnitt gespeichert werden.

Cap'n'Proto-Typen

Der Standard würde eine interne Darstellung der Schemasprache von

```Cap'n Proto
struct Person {
Name @0 :Text;
Geburtsdatum @3 :Datum;

E-Mail @1 :Text;
Telefone @2 :Liste (Telefonnummer);

struct PhoneNumber {
Zahl @0 :Text;
Typ @1 :Typ;

enum Type {
  mobile @0;
  home @1;
  work @2;
}

}
}

struct Datum {
Jahr @0 :Int16;
Monat @1 :UInt8;
Tag @2 :UInt8;
}

might be represented as

```wasm
(<strong i="32">@capnproto</strong> type $Date (struct
    (field "year" Int16)
    (field "month" UInt8)
    (field "day" UInt8)
))
(<strong i="33">@capnproto</strong> type $Person_PhoneNumber_Type (enum 0 1 2))
(<strong i="34">@capnproto</strong> type $Person_PhoneNumber (struct
    (field "number" Text)
    (field "type" $Person_PhoneNumber_Type)
))
(<strong i="35">@capnproto</strong> type $Person (struct
    (field "name" Text)
    (field "email" Text)
    (field "phones" (generic List $Person_PhoneNumber))
    (field "birthdate" $Data)
))

Serialisieren aus dem linearen Speicher

Capnproto-Nachrichten übergeben zwei Arten von Daten: Segmente (Rohbytes) und Fähigkeiten.

Diese entsprechen grob dem linearen Speicher und den Tabellen von WebAssembly. Die einfachste Möglichkeit für Webassembly, CapnProto-Nachrichten zu erstellen, wäre, einen Offset und eine Länge für Segmente an den linearen Speicher und einen Offset und eine Länge für Fähigkeiten an eine Tabelle zu übergeben.

(Für Fähigkeiten könnte ein besserer Ansatz entwickelt werden, um Laufzeittypprüfungen zu vermeiden.)

Beachten Sie, dass die eigentlichen Serialisierungsberechnungen, wenn überhaupt, im Glue-Code stattfinden würden (siehe Generieren des Glue-Codes ).

Bindungsoperatoren

| Betreiber | Sofort | Kinder | Beschreibung |
| :--- | :--- | :--- | :--- |
| Segment | off‑idx
len‑idx | | Nimmt die off-idx 'ten und len-idx 'ten wasm-Werte des Quelltupels, die beide i32 s sein müssen, als Offset und Länge eines Stücks des linearen Speichers in in dem ein Segment gespeichert wird. |
| gefangen | off‑idx
len‑idx | | Nimmt die off-idx 'ten und len-idx 'ten wasm-Werte des Quelltupels, die beide i32 s sein müssen, als Offset und Länge eines Tabellenabschnitts, in dem die Fähigkeitstabelle wird gespeichert. |
| Nachricht | Capnproto-Typ
Fähigkeitstabelle | Segmente | Erstellt eine capnproto-Nachricht im Format capnproto-type , unter Verwendung der bereitgestellten Funktionstabelle und der Segmente. |

Serialisieren aus verwaltetem Speicher

Es ist schwierig, ein bestimmtes Verhalten festzulegen, bevor der GC-Vorschlag landet. Die allgemeine Implementierung ist jedoch, dass Capnproto-Bindungen einen einzigen Konvertierungsoperator verwenden würden, um Capnproto-Typen von GC-Typen abzurufen.

Die Konvertierungsregeln für Low-Level-Typen wären ziemlich einfach: i8 konvertiert in Int8, UInt8 und bool, i16 konvertiert in Int16 usw. High-Level-Typen würden in ihre capnproto-Äquivalente konvertiert: Struktur- und Array-Referenzen werden in Zeiger konvertiert, undurchsichtige Referenzen in Fähigkeiten umwandeln.

Ein vollständigerer Vorschlag müsste eine Strategie für enum und Gewerkschaften definieren.

Bindungsoperatoren

| Betreiber | Sofort | Kinder | Beschreibung |
| :--- | :--- | :--- | :--- |
| als | Capnproto-Typ
idx | | Nimmt den idx -ten wasm-Wert des Quelltupels, der eine Referenz sein muss, und erzeugt einen Capnproto-Wert von capnproto-type . |

Deserialisieren in linearen Speicher

Das Deserialisieren in linearen Speicher ähnelt größtenteils dem Serialisieren davon, mit einem zusätzlichen Vorbehalt: Der Wasm-Code weiß oft nicht im Voraus, wie viel Speicher der Capnproto-Typ benötigt, und muss dem Host eine Art dynamische Speicherverwaltungsmethode zur Verfügung stellen .

Im Vorschlag für WebIDL-Bindungen besteht die vorgeschlagene Lösung darin, Allocator-Callbacks an die Hostfunktion zu übergeben. Für Capnproto-Bindungen wäre diese Methode unzureichend, da dynamische Zuweisungen sowohl auf der Seite des Aufrufers als auch auf der Seite des Aufrufers erfolgen müssen.

Eine andere Lösung wäre, eingehenden Binding-Maps zu erlauben, sich an zwei eingehende Binding-Ausdrücke (und damit an zwei Funktionen) zu binden: eine, die den Speicher für die Capnproto-Daten zuweist, und eine, die die Daten tatsächlich übernimmt.

Deserialisieren in verwalteten Speicher

Das Deserialisieren in verwalteten Speicher würde dieselbe Art von Konvertierungsoperator verwenden wie die entgegengesetzte Richtung.

Generieren des Leimcodes

Beim Verknüpfen zweier wasm-Module (ob statisch oder dynamisch) sollte der Embedder alle Capnproto-Typen auflisten, die beiden Modulen gemeinsam sind, Bindungen zwischen Funktionstypen und Capnproto-Typen, und Glue-Code zwischen jedem verschiedenen Paar von Funktionstypen generieren.

Der Klebecode würde von den Typen der gebundenen Daten abhängen. Glue-Code zwischen linearen Speicherbindungen würde auf memcpy-Aufrufe hinauslaufen. Glue-Code zwischen verwalteten Speicherbindungen würde auf die Übergabe von Referenzen hinauslaufen. Andererseits würde Glue-Code zwischen linearem und verwaltetem Speicher kompliziertere verschachtelte Konvertierungsoperationen beinhalten.

Beispielsweise könnte ein Java-Modul eine Funktion exportieren, die Argumente als GC-Typen verwenden und diese Funktion an eine typisierte Signatur binden; der Interpreter sollte einem Python-Modul und einem C++ erlauben, diese Typsignatur zu importieren; die C++-Bindung würde Daten aus dem linearen Speicher übergeben, während die Python-Bindung Daten aus dem GC-Speicher übergeben würde. Die notwendigen Konvertierungen wären für die Java-, Python- und C++-Compiler

Alternative Lösungen

In diesem Abschnitt untersuche ich alternative Möglichkeiten zum Austausch von Daten und wie sie mit den im Abschnitt Anforderungen definierten Metriken bewertet werden.

JSON-Nachrichten austauschen

Es ist die Brute-Force-Lösung. Ich werde nicht zu viel Zeit damit verbringen, weil seine Mängel ziemlich offensichtlich sind. Es erfüllt nicht die Anforderungen 2, 4 und 6.

Senden Sie in einem Serialisierungsformat codierte Rohbytes

Es ist eine Teillösung. Definieren Sie eine Möglichkeit für wasm-Module, Segmente von linearem Speicher und Tabellen an andere Module zu übergeben. und verwenden Sie dasselbe Format, um es zu decodieren.

Es übergibt 1 und 3, und es kann mit einigen Anpassungen 2 und 4 übergeben (zB die Referenzen als Indizes an eine Tabelle übergeben). Es kann 6 übergeben, wenn der Benutzer sicherstellt, dass der Serialisierungstyp in eine Typdefinition in der Sprache des Aufrufers exportiert wird.

Es schlägt jedoch bei den Anforderungen 5 und 7 fehl. Es ist unpraktisch, wenn es zwischen zwei GC-Implementierungen bindet; Zum Beispiel müsste ein Python-Modul, das eine Java-Bibliothek mit über Protobuf aufruft, ein Wörterbuch als linearen Speicher serialisieren, diesen Speicherabschnitt übergeben und dann als Java-Objekt deserialisieren, anstatt ein paar Hashtable-Lookups durchzuführen, die in wegoptimiert werden können eine JIT-Implementierung.

Und es ermutigt jeden Bibliotheksautor, sein eigenes Serialisierungsformat (JSON, Protobuf, FlatBuffer, Cap'n Proto, SBE) zu verwenden, was für die Interoperabilität nicht ideal ist; obwohl dies durch die Definition eines kanonischen Serialisierungsformats in Werkzeugkonventionen gemildert werden könnte.

Das Hinzufügen der Möglichkeit, beliebige Slices des linearen Speichers zu übergeben, wäre jedoch ein guter erster Schritt.

GC-Objekte senden

Es wäre möglich, sich darauf zu verlassen, dass sich Module gegenseitig GC-Objekte senden.

Die Lösung hat einige Vorteile: Der GC-Vorschlag ist bereits in Arbeit; es übergibt 1, 3, 4 und 7. GC-erfasste Daten sind teuer in der Zuweisung, aber billig in der Weitergabe.

Diese Lösung ist jedoch nicht ideal für C-ähnliche Sprachen. Zum Beispiel müsste ein D-Modul, das Daten an ein Rust-Modul übergibt, seine Daten in einen GC-Graphen serialisieren, den Graphen an die Rust-Funktion übergeben, die ihn in seinen linearen Speicher deserialisieren würde. Dieser Prozess weist GC-Knoten zu, die sofort verworfen werden, was viel unnötigen Overhead verursacht.

Abgesehen davon hat der aktuelle GC-Vorschlag keine integrierte Unterstützung für Enumerationen und Gewerkschaften; und die Fehlerbehandlung würde entweder zur Linkzeit oder zur Laufzeit statt zur Kompilierzeit erfolgen, es sei denn, der Compiler kann wasm-GC-Typen lesen und verstehen.

Andere Codierungen verwenden

Jede Serialisierungsbibliothek, die ein Typsystem definiert, könnte für wasm funktionieren.

Capnproto scheint aufgrund seiner Betonung auf Nullkopien und seiner eingebauten Objektfähigkeiten, die sauber auf Referenztypen abbilden, am geeignetsten.

Verbleibende Arbeit

Die folgenden Konzepte müssten konkretisiert werden, um aus diesem einfachen Vorschlag ein Dokument zu machen, das der Community Group vorgelegt werden kann.

  • Bindungsoperatoren
  • Äquivalenzen des GC-Typs
  • Objektfähigkeiten
  • Boolesche Arrays
  • Arrays
  • Konstanten
  • Generika
  • Typenentwicklung
  • Fügen Sie einen dritten Bindungstyp "Getter und Setter" hinzu.
  • Mögliche Caching-Strategien
  • Unterstützung für mehrere Tabellen und lineare Speicher

In der Zwischenzeit wäre jedes Feedback zu dem, was ich bereits geschrieben habe, willkommen. Der Umfang hier ist ziemlich groß, daher würde ich mich freuen, wenn ich helfen könnte, die Fragen einzugrenzen, die dieser Vorschlag beantworten muss.

Hilfreichster Kommentar

Wir können einige Bindungen pro IR-Typ hinzufügen, um die überwiegende Mehrheit der Sprachen abzudecken.

Dies ist die entscheidende zugrunde liegende Annahme, von der ich glaube, dass sie einfach nicht wahr ist. Meine Erfahrung ist, dass es (mindestens!) so viele Darstellungsmöglichkeiten gibt wie es Sprachimplementierungen gibt. Und sie können beliebig kompliziert sein.

Nehmen wir V8, das allein einige Dutzend(!) Repräsentationen für Strings hat, einschließlich verschiedener Codierungen, heterogener Seile usw.

Der Fall Haskell ist weitaus komplizierter als Sie beschreiben, da Listen in Haskell faul sind, was bedeutet, dass Sie für jedes einzelne Zeichen in einer Zeichenfolge möglicherweise einen Thunk aufrufen müssen.

Andere Sprachen verwenden lustige Darstellungen für die Länge einer Zeichenfolge oder speichern sie nicht explizit, sondern erfordern eine Berechnung.

Diese beiden Beispiele zeigen bereits, dass ein deklaratives Datenlayout nicht ausreicht, Sie müssen oft in der Lage sein, Laufzeitcode aufzurufen, der wiederum seine eigenen Aufrufkonventionen haben kann.

Und das sind nur Strings, die konzeptionell ein recht einfacher Datentyp sind. Ich möchte gar nicht an die unendlichen Möglichkeiten denken, wie Sprachen Produkttypen (Tupel/Strukturen/Objekte) darstellen.

Und dann ist da die Empfangsseite, wo Sie all diese Datenstrukturen erstellen können müssen!

Daher halte ich es für völlig unrealistisch, dass wir jemals auch nur annähernd die "überwiegende Mehrheit der Sprachen" unterstützen würden. Stattdessen würden wir anfangen, einige zu privilegieren, während wir bereits einen großen Zoo mit willkürlichem Zeug anbauen. Das scheint auf mehreren Ebenen fatal.

Alle 61 Kommentare

Das ist wirklich interessant! Ich habe mich nur schnell durchgelesen und habe nur einige erste Gedanken, aber meine erste und wichtigste Frage wäre, warum die bestehenden FFI-Mechanismen, die die meisten Sprachen bereits bereitstellen / verwenden, für WebAssembly nicht ausreichen. Praktisch jede mir bekannte Sprache verfügt über eine Form von C FFI und ist daher bereits heute interoperabel. Viele dieser Sprachen können auch statische Typprüfungen basierend auf diesen Bindungen durchführen. Darüber hinaus gibt es bereits viele Tools rund um diese Schnittstellen (z. B. die bindgen Kiste für Rust, erl_nif für Erlang/BEAM usw.). C FFI adressiert bereits die wichtigsten Anforderungen und hat den entscheidenden Vorteil, dass es sich bereits vielfach in der Praxis bewährt und eingesetzt hat.

5 - Brücke zwischen Speicherlayouts

Ein ideales Typsystem sollte semantische Typen ausdrücken und Sprachen entscheiden lassen, wie sie im Gedächtnis interpretiert werden. Während die Weitergabe von Daten zwischen Sprachen mit inkompatiblen Speicherlayouts immer mit einem gewissen Overhead verbunden ist, sollte die Weitergabe von Daten zwischen ähnlichen Sprachen idealerweise kostengünstig sein (zB sollten Einbetter Serialisierungs-Deserialisierungsschritte vermeiden, wenn ein Memcpy die gleiche Aufgabe erfüllen kann).

Die Konvertierungsarbeit beim Übergeben von Daten zwischen zwei Modulen sollte für den Entwickler transparent sein, solange die semantischen Typen kompatibel sind.

Die transparente Übersetzung eines Layouts in ein anderes beim Übergeben von Daten über die FFI-Barriere scheint mir wirklich eine Aufgabe für Compiler-Backends oder Sprachlaufzeiten zu sein und in leistungsempfindlichen Sprachen wie C/C++/Rust/etc. wahrscheinlich überhaupt nicht wünschenswert. Insbesondere für Dinge, die Sie über FFI hin- und herleiten möchten, scheint es mir immer besser zu sein, eine gemeinsame ABI zu verwenden, als jede Art von Übersetzung vorzunehmen, da die Übersetzung wahrscheinlich zu hohe Kosten verursachen würde. Der Vorteil, ein anderes Layout als das übliche ABI der Plattform zu wählen, wird sich wahrscheinlich nicht lohnen, aber ich gebe gerne zu, dass ich möglicherweise falsch verstehe, was Sie mit alternativen Layouts meinen.

Abgesehen davon hat die Belastung von Compilern/Laufzeiten mit soliden FFI-Tools einen zusätzlichen Vorteil, da alle vorgenommenen Verbesserungen auf andere Plattformen anwendbar sind und umgekehrt, da Verbesserungen der FFI für Nicht-Wasm-Plattformen Wasm zugute kommen. Ich denke, das Argument muss wirklich überzeugend sein, um im Wesentlichen von vorne zu beginnen und einen neuen FFI-Mechanismus aufzubauen.

Entschuldigung, wenn ich den Zweck des Vorschlags missverstanden oder etwas Wichtiges übersehen habe, wie oben erwähnt, muss ich es noch einmal genauer durchlesen, aber ich hatte das Gefühl, dass ich meine ersten Fragen stellen musste, während ich etwas Zeit hatte.

Dafür gibt es auch Apache Arrow , der sich jedoch mehr auf Hochleistungsanwendungen konzentriert.

Ich glaube, ich stimme der allgemeinen Motivation hier zu und sie stimmt im Grunde mit den Diskussionen überein, die wir darüber geführt haben, wie Web-IDL-Bindungen in Zukunft verallgemeinert werden könnten. Tatsächlich enthielten frühere Entwürfe des Erklärers einen FAQ-Eintrag, der diesen intersprachlichen Anwendungsfall erwähnte.

Mein Hauptanliegen (und Grund für das Weglassen dieses FAQ-Eintrags) ist der Umfang: Das allgemeine Problem der Bindung von N Sprachen scheint wahrscheinlich eine Menge offener (und möglicherweise nicht abschließender) Diskussionen zu erzeugen, insbesondere da es niemand bereits tut ( was natürlich ein Henne-Ei-Problem ist). Im Gegensatz dazu sind die von Web IDL Bindings adressierten Probleme ziemlich konkret und werden heute mit Rust/C++ leicht demonstriert, was es uns ermöglicht, die (nicht trivialen) Bemühungen zur Standardisierung/Implementierung zu motivieren und auch eifrig die vorgeschlagene Lösung zu prototypieren/zu validieren.

Aber ich hoffe, dass Web-IDL-Bindungen es uns ermöglichen, dieses Henne-Ei-Problem zu lösen und Erfahrungen mit der Sprachbindung zu sammeln, die eine nächste Erweiterungswelle oder etwas Neues und nicht Web-IDL-spezifisches motivieren könnten. (Beachten Sie, dass, wie derzeit vorgeschlagen, ein Optimierungs-Impl die hier erwähnten Optimierungen durchführen kann, wenn zwei wasm-Module, die kompatible Web-IDL-Bindungen verwenden, sich gegenseitig aufrufen; nur ohne die volle Ausdruckskraft von Cap'n Proto.)

Ich sollte vorab sagen, dass ich noch nicht die Zeit hatte, den Vorschlag vollständig zu groken.
Der Grund dafür ist, dass ich die Aufgabe für unmöglich halte. Dafür gibt es zwei wesentliche Gründe:
A. Unterschiedliche Sprachen haben unterschiedliche Semantiken, die nicht unbedingt in einer Typannotation erfasst werden. In diesem Fall unterscheidet sich die Bewertung von Prolog radikal von der Bewertung von C++: bis zu dem Punkt, an dem die Sprachen im Wesentlichen nicht interoperabel sind. (Für Prolog können Sie eine Reihe anderer Sprachen ersetzen)

B. Per Definition ist nicht garantiert, dass ein LCD von Typsystemen die gesamte Typsprache einer gegebenen Sprache erfasst. Das lässt den Sprachimplementierer vor eine zutiefst unbequeme Wahl: entweder seine eigene Sprache unterstützen oder auf die Vorteile des Typsystems seiner Sprache verzichten. Ein typisches Beispiel: Haskell hat "Typklassen". Jede Implementierung von Haskell, die keine Unterstützung von Typklassen beinhaltet, würde es effektiv entkernen und unbrauchbar machen.
Ein weiteres Beispiel: Die Unterstützung von Generika durch C++ erfordert die Eliminierung der Generizität zur Kompilierzeit; auf der anderen Seite verwenden ML, Java (und eine Reihe anderer Sprachen) eine Form der universellen Repräsentation – die mit dem Ansatz von C++ nicht kompatibel ist.

Auf der anderen Seite scheint die Verwendung von zwei Ausdrücken eines exportierten/importierten Typs seine eigenen Probleme aufzuwerfen: Soll das Sprachsystem überprüfen, ob die beiden Ausdrücke in gewisser Weise konsistent sind? Wessen Verantwortung trägt diese Arbeit?

@lukewagner Danke für die Links! Ich bin auf jeden Fall froh, dass ich die Gelegenheit hatte, dieses Dokument zu lesen!

Es scheint mir, als ob in dieser speziellen Diskussion zwei Dinge miteinander vermischt sind - einiges von dem, was unten steht, ist ausgeschrieben, damit ich mein Verständnis noch einmal überprüfen kann.

  1. Effiziente Hostbindungen

    • Grundsätzlich soll WebIDL das Problem lösen, zumindest für Browserumgebungen - eine Schnittstellenbeschreibung, die von Modul->Host und Host->Modul abbildet und im Wesentlichen die Übersetzungsarbeit von einem zum anderen an die Host-Engine delegiert. Diese Übersetzung ist nicht unbedingt ideal oder gar optimiert, aber die Optimierung von Engines kann sie dazu nutzen. Jedoch wird selbst eine optimierte Übersetzung immer noch bis zu einem gewissen Grad durchgeführt, aber dies ist akzeptabel, da die Alternative immer noch eine Übersetzung ist, nur langsamer.

  2. Effiziente heterogene Modul-zu-Modul-Bindungen.

    • Mit anderen Worten, bei zwei Modulen, von denen eines in source und das andere in dest , teilen sich Typen zwischen ihnen, Aufruf von source->dest und/oder dest->source

    • Wenn kein gemeinsames FFI verfügbar ist und etwas wie WebIDL, dh Huckepack auf 1 gegeben ist, wäre der nicht optimierte Weg, über einen gemeinsamen Nennertyp zu übersetzen, der von der Hostumgebung bereitgestellt wird, wenn über Sprachbarrieren hinweg aufgerufen wird, z. B. source type -> common type -> dest type .



      • Eine Optimierungs-Engine könnte diese Übersetzung theoretisch direkt von source auf dest ohne Zwischenschaltung durchführen, verursacht aber immer noch einen Übersetzungsaufwand.



    • Ist ein gemeinsames FFI vorhanden, dh source und dest teilen sich eine ABI (zB C ABI), dann können sich source und dest direkt mit überhaupt kein Overhead, über das FFI. Dies ist wahrscheinlich das wahrscheinlichste Szenario in der Praxis.

Meine Meinung ist also, dass es definitiv Vorteile hat, WebIDL oder etwas Ähnliches zu nutzen (dh eine Obermenge, die eine breitere Menge von Host-APIs/-Umgebungen unterstützt), aber es ist wirklich nur eine Lösung für das in 1 beschriebene Problem und die Untermenge von 2, die sich mit Sprachbindungen befasst, bei denen kein FFI verfügbar ist. Die Teilmenge von 2, bei der FFI verfügbar ist, ist den Alternativen eindeutig vorzuziehen, da sie per se keinen Overhead verursacht.

Gibt es gute Gründe für die Verwendung einer IDL, auch wenn FFI eine Option ist? Um es klar zu sagen, ich bin definitiv damit einverstanden, eine IDL für die anderen erwähnten Anwendungsfälle zu verwenden, aber ich frage speziell im Kontext der Sprachinteroperabilität und nicht der Hostbindungen.

Ein paar zusätzliche Fragen habe ich, wenn sowohl C FFI (als Beispiel, da es am häufigsten vorkommt) als auch IDL gleichzeitig verwendet / vorhanden sind:

  • Wenn sowohl die Sprachen source als auch dest unterschiedliche Typdefinitionen für einen gemeinsam genutzten Typ mit derselben zugrunde liegenden speicherinternen Darstellung gemäß ihrer gemeinsamen ABI bereitstellen (z. B. eine gemeinsame Darstellung für ein Array variabler Länge ) - wird die Host-Engine versuchen, eine Übersetzung zwischen diesen Typen durchzuführen, nur weil die IDL-Direktiven vorhanden sind, obwohl sie sich mit ihrem Standard-FFI sicher gegenseitig aufrufen könnten?

    • Wenn nicht, und es ist Opt-in, scheint dies das ideale Szenario zu sein, da Sie IDL hinzufügen können, um Interop mit Sprachen ohne FFI zu unterstützen, während gleichzeitig Sprachen mit FFI unterstützt werden. Ich bin mir jedoch nicht sicher, wie eine Host-Engine das zum Laufen bringen würde. Ich habe es noch nicht ganz durchdacht, also übersehe ich wahrscheinlich etwas

    • Wenn ja, wie vereinheitlicht die Host-Engine Typen?:



      • Wenn sich die Engine nur um das Layout kümmert, wie kann die statische Analyse dann erkennen, wenn ein Anrufer einem Angerufenen falsche Argumenttypen bereitstellt? Wenn diese Art der Analyse kein Ziel ist, scheint IDL wirklich nur ideal für Hostbindungen geeignet zu sein, und weniger sprachübergreifend.


      • Wenn dem Motor mehr als das Layout wichtig ist, d. h. das Typensystem erfordert sowohl nominale als auch strukturelle Kompatibilität:





        • Wer definiert den autoritativen Typ für eine Funktion? Wie kann ich überhaupt auf den maßgeblichen Typ aus einer Sprache verweisen? Nehmen wir zum Beispiel an, ich rufe eine in einer anderen Sprache geschriebene Shared Library auf, die eine add/2 Funktion definiert, und add/2 erwartet zwei Argumente vom Typ size_t . Meine Sprache kennt size_t nominell nicht unbedingt, sie hat ihre eigene ABI-kompatible Darstellung von vorzeichenlosen Ganzzahlen der Maschinenbreite, usize , also verwenden die FFI-Bindungen für diese Funktion in meiner Sprache my Sprachentypen. Wie kann mein Compiler angesichts dessen wissen, dass er eine IDL generieren soll, die usize auf size_t abbildet.






  • Gibt es Beispiele für IDL-Schnittstellen, die zum Aufrufen zwischen Modulen in einem Programm verwendet werden, bei denen FFI verfügbar ist, aber explizit zugunsten der von IDL beschriebenen Schnittstelle ungenutzt gelassen wird? Speziell etwas nicht WebAssembly, hauptsächlich daran interessiert, die Vorteile in diesen Fällen zu untersuchen.

Ich gebe zu, dass ich immer noch versuche, die vollständigen Details von WebIDL und seinen Vorgängern durchzugehen, wie das alles mit den verschiedenen Hosts (Browser vs. Nicht-Browser) zusammenpasst und so weiter, lass es mich auf jeden Fall wissen, wenn ich es übersehen habe etwas.

@bitwalker

Das ist wirklich interessant!

Schön, dass es dir gefallen hat!

Meine erste und wichtigste Frage wäre jedoch zu fragen, warum der vorhandene FFI-Mechanismus, den die meisten Sprachen bereits bereitstellen/verwenden, für WebAssembly nicht ausreicht.

Das C-Typ-System hat als Inter-Language-IDL einige Probleme:

  • Es arbeitet unter der Annahme eines gemeinsamen Adressraums, der unsicher ist und absichtlich nicht in WebAssembly enthalten ist. (Meine eigene Erfahrung mit einem JS-to-C-FFI legt nahe, dass Implementierungen dazu neigen, nur Sicherheit gegen Geschwindigkeit zu tauschen)

  • Es bietet keine native Unterstützung für dynamische Längenarrays, getaggte Unions, Standardwerte, Generics usw.

  • Es gibt kein direktes Äquivalent zu Referenztypen.

C++ löst einige dieser Probleme (nicht das größte, gemeinsam genutzter Adressraum), fügt jedoch eine Reihe von Konzepten hinzu, die in IPC nicht wirklich nützlich sind. Natürlich können Sie immer eine Obermenge von C oder eine Untermenge von C++ als Ihre IDL verwenden und dann verbindliche Regeln dafür entwickeln, aber an diesem Punkt haben Sie fast keine Vorteile mehr aus dem vorhandenen Code, also können Sie genauso gut einen vorhandenen verwenden IDL.

Insbesondere für Dinge, die Sie planen, über das FFI hin und her zu gehen

Ich bin nicht ganz so, wie Sie das meinen, aber um es klar zu sagen: Ich glaube nicht, dass veränderliche Daten zwischen Modulen im allgemeinen Fall hin und her übergeben werden können. Dieser Vorschlag versucht, einen Weg zu skizzieren, unveränderliche Daten zu senden und im Gegenzug unveränderliche Daten zu erhalten, zwischen Modulen, die keine Informationen darüber haben, wie die anderen ihre Daten speichern.

Der Vorteil, ein anderes Layout als das übliche ABI der Plattform zu wählen, wird sich wahrscheinlich nicht lohnen, aber ich gebe gerne zu, dass ich möglicherweise falsch verstehe, was Sie mit alternativen Layouts meinen.

Die Sache ist, im Moment ist die gemeinsame ABI ein Stück von Bytes, die im linearen Speicher gespeichert sind. Aber in Zukunft, wenn der GC-Vorschlag implementiert wird, werden einige Sprachen (Java, C#, Python) sehr wenig bis gar nichts im linearen Speicher speichern. Stattdessen speichern sie alle ihre Daten in GC-Strukturen. Wenn zwei dieser Sprachen versuchen zu kommunizieren, wäre das Serialisieren dieser Strukturen in einen Bytestrom, nur um sie sofort zu deserialisieren, unnötiger Overhead.


@KronicDeth Danke, ich werde es mir ansehen.

Obwohl dies nach dem Überfliegen des Dokuments eine Obermenge von Flatbuffern zu sein scheint, die speziell zur Verbesserung der Leistung bestimmt sind? Wie auch immer, welche Eigenschaften können die Interoperabilität von WebAssembly-Modulen im Vergleich zu Flatbuffers oder Capnproto auf einzigartige Weise unterstützen?


@lukewagner

Aber ich hoffe, dass Web-IDL-Bindungen es uns ermöglichen, dieses Henne-Ei-Problem zu lösen und Erfahrungen mit der Sprachbindung zu sammeln, die eine nächste Erweiterungswelle oder etwas Neues und nicht Web-IDL-spezifisches motivieren könnten.

Einverstanden. Beim Schreiben dieses Vorschlags ging ich davon aus, dass jede Implementierung von Capnproto-Bindungen auf Feedback von der Implementierung des WebIDL-Vorschlags basieren würde.

Mein Hauptanliegen (und Grund für das Weglassen dieses FAQ-Eintrags) ist der Umfang: Das allgemeine Problem der Bindung von N Sprachen scheint wahrscheinlich eine Menge offener (und möglicherweise nicht abschließender) Diskussionen zu erzeugen, insbesondere da es niemand bereits tut ( was natürlich ein Henne-Ei-Problem ist).

Ich denke jedoch, dass die Diskussion über eine Capnproto-Implementierung schon so früh von Wert ist.

Insbesondere habe ich versucht zu skizzieren, welche Anforderungen die Implementierung erfüllen sollte/könnte. Ich denke, es wäre auch nützlich, allgemeine Anwendungsfälle aufzulisten, die ein System mit intersprachlichen Typen anzugehen versuchen könnte.

In Bezug auf das N-zu-N-Problem konzentriere ich mich auf diese Lösungen:

  • Sorgen Sie sich nur um die Datenübertragung im RPC-Stil. Versuchen Sie nicht, gemeinsam genutzte veränderliche Daten, Klassen, Zeigerlebensdauern oder andere Arten von Informationen zu übergeben, die komplizierter sind als "ein Vektor hat drei Felder: 'x', 'y' und 'z', die alle Gleitkommazahlen sind".

  • Versuchen Sie, Sprachen und Anwendungsfälle in "Cluster" von Datenverarbeitungsstrategien zu gruppieren. Etablieren Sie Strategien im Zentrum dieser Cluster; Sprachcompiler binden an eine bestimmte Strategie, und der Interpreter erledigt den Rest der NxN-Arbeit.


@fgmccabe

Der Grund dafür ist, dass ich die Aufgabe für unmöglich halte. Dafür gibt es zwei wesentliche Gründe:
A. Unterschiedliche Sprachen haben unterschiedliche Semantiken, die nicht unbedingt in einer Typannotation erfasst werden. In diesem Fall unterscheidet sich die Bewertung von Prolog radikal von der Bewertung von C++: bis zu dem Punkt, an dem die Sprachen im Wesentlichen nicht interoperabel sind. (Für Prolog können Sie eine Reihe anderer Sprachen ersetzen)

Jede Implementierung von Haskell, die keine Unterstützung von Typklassen beinhaltet, würde es effektiv entkernen und unbrauchbar machen.

Ja, die Idee ist nicht, eine perfekte Abstraktion "leicht kompatibel mit allen Sprachen" zu definieren.

Trotzdem denke ich, dass die meisten Sprachen einige Ähnlichkeiten in der Strukturierung ihrer Daten haben (z beliebiger Größe").

Ich denke, es ist möglich, diese Ähnlichkeiten zu nutzen, um die Reibung zwischen den Modulen erheblich zu reduzieren. (siehe auch meine antwort an lukewagner)

B. Per Definition ist nicht garantiert, dass ein LCD von Typsystemen die gesamte Typsprache einer gegebenen Sprache erfasst. Das lässt den Sprachimplementierer vor eine zutiefst unbequeme Wahl: entweder seine eigene Sprache unterstützen oder auf die Vorteile des Typsystems seiner Sprache verzichten.

Ja. Ich denke, die Faustregel lautet hier: "Wenn es sich um eine gemeinsame Bibliotheksgrenze handelt, machen Sie es zu einem Capnproto-Typ, andernfalls verwenden Sie Ihre nativen Typen".

Auf der anderen Seite scheint die Verwendung von zwei Ausdrücken eines exportierten/importierten Typs seine eigenen Probleme aufzuwerfen: Soll das Sprachsystem überprüfen, ob die beiden Ausdrücke in gewisser Weise konsistent sind? Wessen Verantwortung trägt diese Arbeit?

Ja, ich wollte ursprünglich einen Abschnitt über die Invariantenprüfung und einen weiteren über die Typkompatibilität aufnehmen, aber ich verlor den Mut.

Die Antwort auf "Wessen Verantwortung ist es" ist normalerweise "der Angerufene" (weil er davon ausgehen muss, dass alle Daten, die er erhält, verdächtig sind), aber die Überprüfungen könnten entfallen, wenn der Interpreter nachweisen kann, dass der Anrufer die Typinvarianten respektiert.

Das C-Typ-System hat als Inter-Language-IDL einige Probleme

Nur um es klarzustellen, ich schlage es nicht als IDL vor. Vielmehr schlage ich vor, dass die binäre Schnittstelle (die C ABI) bereits existiert, gut definiert ist und bereits umfangreiche Sprachunterstützung bietet. Daraus folgt, dass WebAssembly keine weitere Lösung bereitstellen muss, es sei denn, das zu lösende Problem geht über die sprachübergreifende Interoperabilität hinaus.

Es arbeitet unter der Annahme eines gemeinsamen Adressraums, der unsicher ist und absichtlich nicht in WebAssembly enthalten ist.

Ich glaube, ich sehe hier einen Teil des Missverständnisses. Es gibt zwei Klassen von FFI, über die wir hier sprechen, eine, die die gemeinsame Nutzung von linearem Speicher beinhaltet (traditionelleres FFI mit gemeinsamem Speicher), und eine, die dies nicht tut (traditionellerer IPC/RPC). Ich habe über Ersteres gesprochen, und ich denke, Sie konzentrieren sich mehr auf Letzteres.

Die gemeinsame Nutzung von Speicher zwischen Modulen, wenn Sie die Kontrolle über sie haben (z. B. wenn Sie mehrere unabhängige Module als Teil einer Gesamtanwendung miteinander verknüpfen) ist aus Effizienzgründen wünschenswert, geht jedoch auf Kosten der Sicherheit. Auf der anderen Seite ist es möglich, einen bestimmten linearen Speicher speziell für FFI gemeinsam zu nutzen, obwohl ich nicht weiß, wie praktisch das mit den heutigen Standardwerkzeugen ist.

Modulübergreifendes Interop, das _kein_ Shared Memory FFI verwendet, dh IPC/RPC, scheint definitiv eine gute Ergänzung für WebIDL, Capnproto oder einen der anderen Vorschläge in dieser Richtung zu sein, da dies ihr Brot und Butter ist.

Der Teil, bei dem ich mir dann nicht sicher bin, ist, wie man die beiden Kategorien so mischt, dass Sie die Vorteile von beiden nicht opfern, da die Entscheidung für den einen oder anderen Weg stark vom Anwendungsfall abhängt. Zumindest wie gesagt, es scheint, als könnten wir nur das eine oder das andere haben, wenn es möglich ist, beides zu unterstützen, denke ich, dass das ideal wäre.

Es bietet keine native Unterstützung für dynamische Längenarrays, getaggte Unions, Standardwerte, Generics usw.

Ich denke, das ist jetzt wahrscheinlich nicht relevant, da ich merke, dass wir über zwei verschiedene Dinge sprachen, aber nur für die Nachwelt: Die ABI hat sicherlich eine _Repräsentation_ für Arrays variabler Länge und getaggte Unions, aber Sie haben Recht, C hat eine schwaches Typsystem, aber das ist nicht wirklich der Punkt, Sprachen zielen nicht auf C FFI für das C-Typsystem ab. Der Grund, warum das C ABI nützlich ist, ist, dass es einen gemeinsamen Nenner bietet, den Sprachen verwenden können, um mit anderen zu kommunizieren, die möglicherweise kein Konzept des Typsystems haben, mit dem sie interagieren. Das Fehlen von Systemfunktionen auf höherer Ebene ist nicht ideal und schränkt die Art von Dingen ein, die Sie über FFI ausdrücken können, aber die Einschränkungen sind auch ein Grund dafür, warum es so erfolgreich ist, so ziemlich jede Sprache kann einen Weg finden die ihm ausgesetzten Dinge über diese Schnittstelle darzustellen und umgekehrt.

C++ löst einige dieser Probleme (nicht das größte, gemeinsam genutzter Adressraum), fügt jedoch eine Reihe von Konzepten hinzu, die in IPC nicht wirklich nützlich sind. Natürlich können Sie immer eine Obermenge von C oder eine Untermenge von C++ als Ihre IDL verwenden und dann verbindliche Regeln dafür entwickeln, aber an diesem Punkt haben Sie fast keine Vorteile mehr aus dem vorhandenen Code, also können Sie genauso gut einen vorhandenen verwenden IDL.

Zugegeben, für IPC/RPC ist C eine schreckliche Sprache zum Definieren von Schnittstellen.

Die Sache ist, im Moment ist die gemeinsame ABI ein Stück von Bytes, die im linearen Speicher gespeichert sind.

Das ist sicherlich die Primtive, mit der wir arbeiten, aber das C ABI definiert darüber hinaus viel.

Aber in Zukunft, wenn der GC-Vorschlag implementiert wird, werden einige Sprachen (Java, C#, Python) sehr wenig bis gar nichts im linearen Speicher speichern. Stattdessen speichern sie alle ihre Daten in GC-Strukturen. Wenn zwei dieser Sprachen versuchen zu kommunizieren, wäre das Serialisieren dieser Strukturen in einen Bytestrom, nur um sie sofort zu deserialisieren, unnötiger Overhead.

Ich bin nicht davon überzeugt, dass diese Sprachen darauf springen, GC an den Host zu verschieben, aber das ist nur Spekulation meinerseits. In jedem Fall könnten sich Sprachen, die die von der Host-GC verwalteten Strukturen verstehen, genauso einfach für eine gemeinsame Darstellung dieser Strukturen mit C ABI entscheiden, wie sie mit capnproto dargestellt werden könnten, der einzige Unterschied besteht darin, wo die Spezifikation dieser Darstellung lebt. Abgesehen davon habe ich nur eine sehr schwache Vorstellung von den Details des GC-Vorschlags und wie dieser mit dem Vorschlag für die Hostbindungen zusammenhängt.

TL;DR: Ich denke, wir sind uns in Bezug auf Modulinterop einig, bei dem kein gemeinsamer linearer Speicher im Spiel ist. Aber ich denke, Shared Memory _ist_ wichtig zu unterstützen, und C ABI ist aufgrund der vorhandenen Sprachunterstützung die vernünftigste Wahl für diesen Anwendungsfall. Ich hoffe, dass dieser Vorschlag in seiner Entwicklung beides unterstützt.

Was wir brauchen, ist einfach eine maximal effiziente Möglichkeit zum Austauschen von Bytepuffern und eine Möglichkeit für Sprachen, sich auf das Format zu einigen. Es ist nicht erforderlich, dies auf ein bestimmtes Serialisierungssystem festzulegen. Wenn Cap'n Proto für diesen Zweck am besten geeignet ist, kann es als gewöhnlicher Ausfall organisch entstehen, anstatt von wasm vorgeschrieben zu werden.

Ich bin natürlich voreingenommen, da ich FlatBuffers gemacht

Es gibt viele andere Formate, die diesen beiden in bestimmten Anwendungsfällen vorzuziehen sind.

Beachten Sie, dass sowohl Cap'n Proto als auch FlatBuffers Zero-Copy sind, wahlfreier Zugriff und effizient beim Verschachteln von Formaten sind (d Kommunikation. Sie können sich eine IDL vorstellen, mit der Sie sehr genaue Byte-Layouts für einen Puffer angeben können, einschließlich "die folgenden Bytes sind Cap'n Proto-Schema X".

Während ich subtile Eigenwerbung mache , könnte ich die Leute auf

@aardappel

Was wir brauchen, ist einfach eine maximal effiziente Möglichkeit zum Austauschen von Bytepuffern und eine Möglichkeit für Sprachen, sich auf das Format zu einigen. Es ist nicht erforderlich, dies auf ein bestimmtes Serialisierungssystem festzulegen. Wenn Cap'n Proto für diesen Zweck am besten geeignet ist, kann es als gewöhnlicher Ausfall organisch entstehen, anstatt von wasm vorgeschrieben zu werden.

Ich verstehe den impliziten Punkt, dass wasm nicht dazu verwendet werden sollte, einen anderen seinen Konkurrenten aufzuzwingen, und es ist mir persönlich gleichgültig, welche IDL ausgewählt wird.

Das heißt, wenn alles gesagt und getan ist, muss der Gummi irgendwann auf die Straße treffen. Wenn wasm die Kommunikation zwischen den Sprachen erleichtern möchte (was zugegebenermaßen keine Annahme ist, die alle teilen), dann braucht es ein Standardformat, das mehr ausdrücken kann als "diese Bytes bilden Zahlen". Dieses Format kann Capnproto, C-Strukturen, Flatbuffers oder sogar etwas Spezifisches für wasm sein, aber es kann aus den Gründen, die @fgmccabe skizziert hat, keine Teilmenge von all diesen gleichzeitig sein.

Während ich subtile Eigenwerbung mache, könnte ich die Leute auf FlexBuffers hinweisen, die so etwas wie schemalose FlatBuffers sind. Es hat die gleichen wünschenswerten Zero-Copy-, Random-Access- und billigen Verschachtelungseigenschaften, kann es jedoch ermöglichen, dass Sprachen kommunizieren, ohne sich auf ein Schema zu einigen, ohne Codegen durchzuführen, ähnlich wie bei der Verwendung von JSON.

Ich sehe den Reiz, ich glaube, das ist nicht das, was Sie die meiste Zeit wollen, wenn Sie eine Bibliothek schreiben. Das Problem mit JSON (abgesehen von der schrecklichen Parse-Zeit) besteht darin, dass Sie beim Schreiben eines JSON-Objekts irgendwo in Ihrem Code viel bereinigenden Code schreiben, bevor Sie Ihre Daten verwenden können, z.

assert(myObj.foo);
assert(isJsonObject(myObj.foo));
assert(myObj.foo.bar);
assert(isString(myObj.foo.bar));
loadUrl(myObj.foo.bar);

mit potenziellen Sicherheitslücken, wenn Sie dies nicht tun.

Siehe auch 6 - Fehlerbehandlung während der Kompilierung oben.


@bitwalker

Richtig, ich habe die Möglichkeit eines gemeinsamen linearen Speichers nicht wirklich in Betracht gezogen. Ich würde jemanden brauchen, der besser mit Webassembly-Design vertraut ist als ich ( @lukewagner ?), um zu besprechen, wie machbar es ist und ob es eine gute Möglichkeit ist, Aufrufe zwischen Modulen zu erreichen; es würde auch davon abhängen, auf wie viele Annahmen sich FFIs verlassen, die durch das Speicherlayout von wasm ungültig gemacht werden.

Zum Beispiel verlassen sich FFIs oft darauf, dass ihre Hostsprache die C-Bibliothek verwendet, und geben nativen Bibliotheken direkten Zugriff auf die malloc-Funktion. Wie gut lässt sich diese Strategie im Kontext von zwei sich gegenseitig verdächtigen Modulen auf Wasm übertragen?

Ich denke, ich sollte als Schöpfer von Cap'n Proto etwas zu diesem Thread sagen, aber seltsamerweise habe ich nicht festgestellt, dass ich eine große Meinung habe. Lassen Sie mich ein paar angrenzende Gedanken ausdrücken, die interessant sein können oder auch nicht.

Ich bin auch der technische Leiter von Cloudflare Workers, einer "serverlosen" Umgebung, die JavaScript und WASM ausführt.

Wir haben darüber nachgedacht, Cap'n Proto RPC als Protokoll zu unterstützen, damit Arbeiter miteinander sprechen können. Derzeit sind sie auf HTTP beschränkt, sodass die Messlatte recht niedrig angesetzt ist. :)

Wenn in Workers ein Worker einen anderen aufruft, ist es sehr häufig der Fall, dass beide auf demselben Computer laufen, sogar im selben Prozess. Aus diesem Grund ist eine Zero-Copy-Serialisierung wie Cap'n Proto offensichtlich sehr sinnvoll, insbesondere für WASM-Worker, da sie mit linearem Speicher arbeiten, der theoretisch physisch zwischen ihnen geteilt werden könnte.

Ein zweiter, weniger bekannter Grund, warum wir glauben, dass dies gut passt, ist das RPC-System. Cap'n Proto bietet ein RPC-Protokoll mit voller Objektfähigkeit mit Promise-Pipelining, das dem CapTP nachempfunden ist. Dies macht es einfach, umfangreiche, objektorientierte Interaktionen auf sichere und performante Weise auszudrücken. Cap'n Proto RPC ist nicht nur ein Punkt-zu-Punkt-Protokoll, sondern modelliert Interaktionen zwischen einer beliebigen Anzahl vernetzter Parteien, was unserer Meinung nach eine ziemlich große Sache sein wird.

Inzwischen führt WASI im Land von WASM eine fähigkeitsbasierte API ein. Es scheint, als könnte es hier einige interessante "Synergien" geben.

Trotz alledem sind einige Designziele von Cap'n Proto für den spezifischen Anwendungsfall von FFI möglicherweise nicht sinnvoll:

  • Cap'n Proto-Nachrichten sind so konzipiert, dass sie positionsunabhängig und zusammenhängend sind, sodass sie zwischen Adressräumen übertragen und geteilt werden können. Zeiger sind relativ, und alle Objekte in einer Nachricht müssen im zusammenhängenden Speicher oder zumindest in einer kleinen Anzahl von Segmenten zugewiesen werden. Dies verkompliziert das Nutzungsmodell im Vergleich zu nativen Objekten erheblich. Wenn Sie FFI innerhalb desselben linearen Speicherbereichs verwenden, wird dieser Overhead verschwendet, da Sie native Zeiger problemlos an lose Heap-Objekte übergeben können.
  • Cap'n Proto-Nachrichten sind so konzipiert, dass sie zwischen Schemaversionen vorwärts- und rückwärtskompatibel sind, einschließlich der Möglichkeit, Objekte und Unterbäume verlustfrei zu kopieren, ohne das Schema zu kennen. Dies erfordert, dass einige Lichttypinformationen direkt im Inhalt gespeichert werden, die Cap'n Proto als Metadaten auf jedem Zeiger codiert. Werden gleichzeitig zwei Module kompiliert, die über ein FFI kommunizieren, werden diese Metadaten nicht benötigt.
  • Cap'n Proto RPCs Versprechenspipelining, Pfadverkürzung und Bestellgarantien sind sinnvoll, wenn zwischen einem Anrufer und einem Angerufenen eine nicht zu vernachlässigende Latenzzeit besteht. FFI auf einer einzelnen CPU hat keine solche Latenz, in diesem Fall verschwendet die versprochene Pipelining-Maschinerie wahrscheinlich nur Zyklen.

Kurz gesagt, ich denke, wenn Sie unabhängig bereitgestellte Module in separaten Sandboxen haben, die miteinander kommunizieren, ist Cap'n Proto sehr sinnvoll. Aber für gleichzeitig bereitgestellte Module in einer einzigen Sandbox ist es wahrscheinlich übertrieben.

Danke für die Rückmeldung!

Zeiger sind relativ, und alle Objekte in einer Nachricht müssen im zusammenhängenden Speicher oder zumindest in einer kleinen Anzahl von Segmenten zugewiesen werden. Dies verkompliziert das Nutzungsmodell im Vergleich zu nativen Objekten erheblich. Wenn Sie FFI innerhalb desselben linearen Speicherbereichs verwenden, wird dieser Overhead verschwendet, da Sie native Zeiger problemlos an lose Heap-Objekte übergeben können.

Ich weiß nicht, wie machbar ein Shared-Linear-Memory-Ansatz für wasm ist (siehe oben).

Das heißt, so oder so denke ich nicht, dass der Overhead von relativen Zeigern so schlecht wäre. WebAssembly verwendet bereits Offsets relativ zum Start des linearen Speichers, und Implementierungen haben Tricks, um die ADD Anweisungen in den meisten Fällen wegzuoptimieren (glaube ich), sodass der Aufwand für die Verwendung relativer Zeiger wahrscheinlich auch wegoptimiert werden könnte.

Cap'n Proto-Nachrichten sind so konzipiert, dass sie zwischen Schemaversionen vorwärts- und rückwärtskompatibel sind, einschließlich der Möglichkeit, Objekte und Unterbäume verlustfrei zu kopieren, ohne das Schema zu kennen. [...] Werden zwei Module, die über ein FFI kommunizieren, gleichzeitig kompiliert, dann werden diese Metadaten nicht benötigt.

Ich glaube nicht, dass das stimmt. Die Möglichkeit für Module, abwärtskompatible Typen an ihren Grenzen zu definieren, ermöglicht es wasm, ein Abhängigkeitsbaummodell zu verwenden, während Haskells Abhängigkeitsrautenproblem größtenteils vermieden wird.

Eine größere Quelle sinnlosen Overheads wäre die Art und Weise, wie capnproto xor seine Variablen mit ihren Standardwerten abgleicht, was nützlich ist, wenn Null-Bytes wegkomprimiert werden, aber in Zero-Copy-Workflows kontraproduktiv ist.

Ich weiß nicht, wie machbar ein Shared-Linear-Memory-Ansatz für wasm ist (siehe oben).

Ah, TBH Ich glaube nicht, dass ich genug Kontext habe, um diesem Teil der Diskussion zu folgen. Wenn Sie keinen gemeinsamen Adressraum haben, dann macht Cap'n Proto sehr viel Sinn.

Gerne berate ich Sie bei der Gestaltung solcher Formate. FWIW, es gibt ein paar kleine Dinge, die ich in Cap'n Proto ändern würde, wenn ich mich nicht um die Kompatibilität mit heute bereits existierenden Apps kümmern würde ... es sind jedoch hauptsächlich Details zur Zeigercodierung auf niedriger Ebene.

Eine größere Quelle für sinnlosen Overhead wäre die Art und Weise, wie capnproto seine Variablen mit ihren Standardwerten abgleicht, was nützlich ist, wenn Null-Bytes wegkomprimiert werden, aber in Zero-Copy-Workflows kontraproduktiv ist.

Ein bisschen off-topic, aber die XOR-Sache ist eine Optimierung, kein Overhead, selbst im Zero-Copy-Fall. Es stellt sicher, dass alle Strukturen nullinitialisiert sind, was bedeutet, dass Sie bei der Objektzuweisung keine Initialisierung durchführen müssen, wenn der Puffer bereits null ist (was sowieso oft der Fall wäre). Ein XOR gegen eine Kompilierzeitkonstante kostet wahrscheinlich 1 Zyklus, wohingegen jede Art von Speicherzugriff viel mehr kostet.

@lukewagner Irgendwelche Gedanken zum Teil "linearen Speicher teilen"?

Ich denke, es gibt Anwendungsfälle sowohl für die gemeinsame als auch für die nicht gemeinsame Nutzung von linearem Speicher, und letztendlich müssen Tools beides unterstützen:

Die gemeinsame Nutzung ist dort sinnvoll, wo eine native App heute statische oder dynamische Verknüpfungen verwenden würde: Wenn der gesamte Code, der kombiniert wird, vollständig vertrauenswürdig ist und seine Kombination entweder dieselbe Toolchain oder eine streng definierte ABI verwendet. Es ist jedoch eher ein fragileres Software-Kompositionsmodell.

Bei einer lockerer gekoppelten Sammlung von Modulen, bei denen das klassische Design im Unix-Stil den Code in separate Prozesse stecken würde, die durch Pipes verbunden sind, ist es sinnvoll, den Speicher nicht gemeinsam zu nutzen. Persönlich denke ich, dass dies die aufregendere / futuristischere Richtung für ein kompositorisches Software-Ökosystem ist, und deshalb habe ich mich dafür eingesetzt, dass dies der Standard für jede Toolchain ist, die darauf abzielt, über ESM-Integration am ESM/npm-Ökosystem teilzunehmen (und das tatsächlich .) ist heute bei Rusts wasm-pack/wasm-bindgen der Fall). Die Verwendung eines Mechanismus in der Nähe von Web-IDL-Bindungen oder der von Ihnen vorgeschlagenen Erweiterung ist für mich als eine Form von effizientem, ergonomischem, typisiertem (synchronisiertem oder asynchronem) RPC sehr sinnvoll.

Nachdem ich dies endlich vollständig gelesen habe, klingt es sehr nach meinem Denken in diesem Bereich (was ist dieses Kommentarfeld zu kurz?).

Insbesondere habe ich darüber nachgedacht, dass das Kommunikationsproblem zwischen Modulen am besten mit einem Schema beschrieben wird. Das heißt, wir brauchen nicht das Cap'nProto-Serialisierungsformat, wir können einfach das Schema verwenden. Ich habe derzeit keine Meinung zu der Schemasprache von Cap'nProto.

Aus Sicht von WASI/ESM+npm ist eine Lösung dieser Form für mich am sinnvollsten. Es ist eine Abstraktion über ABIs, ohne von einer gemeinsamen ABI abhängig zu sein. Es ermöglicht im Wesentlichen, eine Schnittstelle mit einer Schema-Lang-API zu beschreiben und über diese Sprachgrenzen hinweg mit nativen ABIs an beiden Enden aufzurufen, sodass der Host die Übersetzung übernehmen kann.

Dies umfasst insbesondere nicht den Anwendungsfall für mehr Koordination mit einem anderen Modul: Wenn Sie sicher sind, dass Sie eine ABI teilen können, können Sie tatsächlich einfach eine ABI verwenden, eine beliebige ABI, sei es C oder Haskell. Wenn Sie alle fraglichen Wasms kontrollieren und kompilieren, ist das ein viel einfacher zu lösendes Problem. Nur wenn Sie in den npm-Fall geraten, in dem Sie beliebigen unbekannten Code laden und dessen Quellsprache nicht kennen, wird so etwas wie Interop auf Schemaebene zwischen Modulen unglaublich attraktiv. Denn wir können entweder das LCD von wasm selbst verwenden – was meiner Meinung nach einem ähnlichen Bogen wie native Bibliotheken folgen wird, und das C ABI verwenden – oder wir können das LCD von Sprachen verwenden, das in der Schemasprache kodiert ist. Und das Schema kann flexibler sein, indem Anforderung 2) zu einer weichen Anforderung gemacht wird, zB sollte es möglich sein, effizient von C zu Rust zu Nim zu konvertieren, aber C zu Haskell mit mehr Overhead ist kein Dealbreaker.

Insbesondere habe ich darüber nachgedacht, dass das Kommunikationsproblem zwischen Modulen am besten mit einem Schema beschrieben wird. Das heißt, wir brauchen kein Serialisierungsformat, wir können einfach das Schema verwenden.

Ich neige dazu, ersterem zuzustimmen, aber ich bin mir nicht sicher, ob letzteres folgt. Wer implementiert das Schema? Selbst wenn der Host den Transport übernimmt, müssen Sie irgendwann definieren, welche Wasm-Werte/Bytes tatsächlich auf beiden Seiten konsumiert/produziert werden, und jedes Modul muss seine eigenen Daten in eine Form bringen, die der Host versteht. Es können sogar mehrere Formulare verfügbar sein, aber das ist einem Serialisierungsformat nicht unähnlich, nur etwas höherwertiger.

es sollte möglich sein, effizient von C zu Rust zu Nim zu konvertieren, C zu Haskell mit mehr Overhead ist kein Dealbreaker.

Vielleicht nicht, aber Sie müssen sich der Auswirkungen bewusst sein. Die Privilegierung von C-ähnlichen Sprachen bedeutet, dass Haskell diese Abstraktion wegen des induzierten Overheads nicht für Haskell-Module verwenden würde. Das wiederum bedeutet, dass es nicht am gleichen "npm"-Ökosystem für seine eigenen Bibliotheken teilnehmen würde.

Und "Haskell" ist hier nur ein Ersatz für so ziemlich jede Hochsprache. Die überwiegende Mehrheit der Sprachen ist nicht C-ähnlich.

Ich behaupte nicht, eine bessere Lösung zu haben, aber ich denke, wir müssen realistisch bleiben, wie effizient und attraktiv jede einzelne ABI- oder Schemaabstraktion für die allgemeine Bevölkerung von Sprachen sein kann, jenseits des üblichen FFI-Stils der Einweg-Interoperabilität. Insbesondere bin ich nicht davon überzeugt, dass ein pan-linguistisches Paket-Ökosystem ein allzu realistisches Ergebnis ist.

Die Privilegierung von C-ähnlichen Sprachen bedeutet, dass Haskell diese Abstraktion wegen des induzierten Overheads nicht für Haskell-Module verwenden würde. Das wiederum bedeutet, dass es nicht am gleichen "npm"-Ökosystem für seine eigenen Bibliotheken teilnehmen würde.

Und "Haskell" ist hier nur ein Ersatz für so ziemlich jede Hochsprache. Die überwiegende Mehrheit der Sprachen ist nicht C-ähnlich.

Könnten einige konkrete Anwendungsfälle geben? Idealerweise vorhandene Bibliotheken in Haskell oder einer anderen Sprache, deren Übersetzung in ein Serialisierungsschema umständlich wäre?

Ich vermute, dass es hauptsächlich auf Utility-Bibliotheken vs. Business-Bibliotheken ankommt. Container, Sortieralgorithmen und andere Dienstprogramme, die auf den Generika der Sprache beruhen, lassen sich beispielsweise nicht gut in wasm übersetzen, aber Parser, GUI-Widgets und Dateisystem-Tools schon.

@PoignardAzur , es ist nicht schwer, sie zu übersetzen, aber es erfordert, dass sie alle Argumente/Ergebnisse an beiden Enden jedes

Speziell bei Haskell besteht zusätzlich das Problem, dass das Kopieren nicht mit der Semantik der Faulheit vereinbar ist. In anderen Sprachen ist sie möglicherweise nicht mit zustandsbehafteten Daten kompatibel.

Wer implementiert das Schema? Selbst wenn der Host den Transport übernimmt, müssen Sie irgendwann definieren, welche Wasm-Werte/Bytes tatsächlich auf beiden Seiten konsumiert/produziert werden, und jedes Modul muss seine eigenen Daten in eine Form bringen, die der Host versteht. Es können sogar mehrere Formulare verfügbar sein, aber das ist einem Serialisierungsformat nicht unähnlich, nur etwas höherwertiger.

Der Host implementiert das Schema. Das Schema beschreibt überhaupt keine Bytes und lässt dies ein Implementierungsdetail sein. Dies ist eine Anlehnung an das Design des WebIDL Bindings-Vorschlags, bei dem das Interessante in den Konvertierungen von C-Strukturen in WebIDL-Typen liegt. Diese Art von Design verwendet Wasm Abstract Interface Types (ich schlage das Akronym: WAIT vor) anstelle von WebIDL-Typen. Im WebIDL-Vorschlag brauchen oder wollen wir keine binäre Darstellung von Daten vorschreiben, wenn sie "in WebIDL" übersetzt wurden, weil wir in der Lage sein möchten, ohne Zwischenstopp direkt von wasm zu Browser-APIs zu gelangen.

Die Privilegierung von C-ähnlichen Sprachen bedeutet, dass Haskell diese Abstraktion wegen des induzierten Overheads nicht für Haskell-Module verwenden würde.

Ach, stimme 100% zu. Ich hätte das Beispiel beenden sollen, um das klarer zu machen: Inzwischen kann Haskell zu Elm zu C# ähnlich effizient sein (vorausgesetzt, sie verwenden wasm gc-Typen), aber C# zu Rust kann Overhead verursachen. Ich glaube nicht, dass es eine Möglichkeit gibt, Overhead zu vermeiden, wenn man über Sprachparadigmen springt.

Ich denke, Ihre Beobachtung ist richtig, dass wir versuchen müssen, die Privilegierung von Sprachen zu vermeiden, denn wenn wir für eine bestimmte Sprache nicht ausreichend ergonomisch und leistungsfähig sind, werden sie nicht so viel Wert in der Verwendung der Schnittstelle sehen und somit nicht am Ökosystem teilnehmen .

Ich glaube, dass wir Hosts viel mehr Spielraum für die Optimierung geben können, indem wir die Typen abstrahieren und kein Wire-Format angeben. Ich denke, es ist kein Ziel zu sagen "C-Strings sind effizient", aber es ist ein Ziel zu sagen "Sprachen, die über C-Style-Strings nachdenken, können dies effizient tun". Oder es sollte kein Format gesegnet sein, aber bestimmte kompatible Aufrufketten sollten effizient sein und alle Aufrufketten sollten möglich sein.

Mit Anrufketten meine ich:

  1. C -> Rost -> Zig -> Fortran, effizient
  2. Haskell -> C# -> Haskell, effizient
  3. C -> Haskell -> Rust -> Schema, ineffizient
  4. Java -> Rost, ineffizient

Und "Haskell" ist hier nur ein Ersatz für so ziemlich jede Hochsprache. Die überwiegende Mehrheit der Sprachen ist nicht C-ähnlich.

Ja, das war meine Absicht, Haskell als konkrete Sprache zu verwenden. (Obwohl Nim wahrscheinlich ein schlechtes Beispiel für eine C-ähnliche Sprache war, da sie auch stark von GC Gebrauch macht)

--

Eine andere Art, wie ich über die abstrakten Typen nachgedacht habe, ist als IR. So wie LLVM eine Viele-zu-Eins-zu-Viele-Beziehung beschreibt (viele Sprachen -> ein IR -> viele Ziele), können wasm-Abstract-Typen eine Viele-zu-Viele-Abbildung von Sprachen+Hosts -> . vermitteln Sprachen + Gastgeber. Etwas in diesem Entwurfsraum nimmt das N^2-Mapping-Problem und verwandelt es in ein N+N-Problem.

Der Host implementiert das Schema.

Nun, das kann nicht genug sein, jedes Modul muss etwas implementieren

Selbst wenn wir das getan haben, ist es immer noch sinnvoll, ein Serialisierungsformat zu definieren, zB für Anwendungen, die Daten zwischen einzelnen Engines übertragen müssen, zB über Netzwerk oder dateibasierte Persistenz.

Nun, das kann nicht genug sein, jedes Modul muss etwas implementieren, damit der Host die Daten finden kann. Wenn der Host ein C-Layout erwartet, müssen Sie dieses C-Layout definieren

Der Host sollte nichts erwarten, aber alles muss unterstützen. Konkreter gesagt , verwenden wir utf8-cstr und utf8-str , die i32 (ptr) bzw. i32 (ptr), i32 (len) annehmen. Es ist nicht erforderlich, in der Spezifikation vorzugeben, dass der Host dies intern als C-Strings darstellt, um sie konkret zuordnen zu können.
Jedes Modul implementiert also etwas, ja, aber die Darstellung der Daten muss nicht in der abstrakten Daten-/Schemaschicht ausgedrückt werden, wodurch wir die Eigenschaft haben, über dieses Datenlayout zu abstrahieren.
Darüber hinaus ist dies auf der Bindungsebene erweiterbar, die zwischen konkreten Wasm-Typen und abstrakten Zwischentypen abbildet. Um Haskell-Unterstützung hinzuzufügen (die Strings sowohl als Kons-Listen von Zeichen als auch als Arrays von Zeichen modelliert), können wir utf8-cons-str und utf8-array-str Bindungen hinzufügen, die Wasm-Typen von (unter Verwendung von aktuellen) erwarten (und validieren). gc-Vorschlagssyntax) (type $haskellString (struct (field i8) (field (ref $haskellString)))) und (type $haskellText (array i8)) .

Das heißt, jedes Modul entscheidet, wie die Daten entstehen. Die abstrakten Typen + Bindungen ermöglichen Konvertierungen zwischen der Art und Weise, wie Module dieselben Daten anzeigen, ohne eine einzelne Darstellung als irgendwie kanonisch zu segnen.

Ein Serialisierungsformat für (eine Teilmenge) der abstrakten Typen wäre nützlich, kann aber als Konsument des Schemaformats implementiert werden und ist meiner Meinung nach ein orthogonales Anliegen. FIDL hat meiner Meinung nach ein Serialisierungsformat für die Teilmenge der Typen, die über das Netzwerk übertragen werden können, die Materialisierung undurchsichtiger Handles nicht erlaubt, während die Übertragung von undurchsichtigen Handles innerhalb eines Systems erlaubt ist (IPC ja, RPC nein).

Was Sie beschreiben, entspricht ziemlich genau dem, was ich mir vorgestellt habe, mit einem großen Vorbehalt: Das Schema muss eine kleine, feste Anzahl möglicher Darstellungen haben. Die Überbrückung zwischen verschiedenen Darstellungen ist ein N*N-Problem, was bedeutet, dass die Anzahl der Darstellungen klein gehalten werden sollte, um eine Überlastung der VM-Schreiber zu vermeiden.

Das Hinzufügen von Haskell-Unterstützung erfordert also die Verwendung vorhandener Bindungen und nicht das Hinzufügen benutzerdefinierter Bindungen.

Einige mögliche Darstellungen:

  • Strukturen und Zeiger im C-Stil.
  • Tatsächliche Capnproto-Bytes.
  • GC-Klassen.
  • Verschlüsse, die als Getter und Setter dienen.
  • Wörterbücher im Python-Stil.

Die Idee dahinter ist, dass, obwohl jede Sprache anders ist und es einige extreme Ausreißer gibt, Sie eine ziemlich große Anzahl von Sprachen in eine relativ kleine Anzahl von Kategorien einordnen können.

Das Hinzufügen von Haskell-Unterstützung erfordert also die Verwendung vorhandener Bindungen und nicht das Hinzufügen benutzerdefinierter Bindungen.

Hängt von der Granularität der vorhandenen Bindungen ab, an die Sie denken. N<->N Sprachen, die jede mögliche Bindung codieren, ist 2*N*N, aber N<->IR ist 2*N, und weiter, wenn Sie sagen N<->[gemeinsame Bindungsstile]<->IR, wobei die Zahl der gängigen Formate ist k, du redest von 2*k, wobei k < N ist.

Insbesondere mit dem von mir beschriebenen Schema erhalten Sie Scheme kostenlos (es würde utf8-cons-str wiederverwenden). Wenn Java Strings auch als char-Arrays modelliert, ist das eine utf8-array-str Bindung. Wenn Nim string_views unter der Haube verwendet, utf8-str . Wenn Zig dem C ABI entspricht, utf8-cstr . (Ich kenne die ABIs von Java/Nim/Zig nicht, daher habe ich sie zuvor nicht als konkrete Beispiele erwähnt)

Also, ja, wir möchten nicht für jede mögliche Sprache eine Bindung hinzufügen, aber wir können einige Bindungen pro IR-Typ hinzufügen, um die überwiegende Mehrheit der Sprachen abzudecken. Ich denke, der Raum für Meinungsverschiedenheiten ist hier: Wie viele Bindungen sind "ein paar", was ist der Sweet Spot, wie streng sollten die Kriterien sein, ob wir die ABI einer Sprache unterstützen?
Auf diese Fragen habe ich keine konkreten Antworten. Ich versuche, viele konkrete Beispiele zu geben, um den Gestaltungsraum besser zu veranschaulichen.

Außerdem möchte ich behaupten, dass wir unbedingt mehrere Bindungen pro abstraktem Typ angeben möchten, um zu vermeiden, dass ein Datentyp privilegiert wird. Wenn die einzige Bindung, die wir für Strings verfügbar machen, utf8-cstr , dann müssen alle Nicht-C-ABI-fähigen Sprachen mit dieser Diskrepanz umgehen. Ich bin damit einverstanden, die Komplexität des VM-Schreibens um einen nicht geringen Faktor zu erhöhen.
Die Gesamtarbeit im Ökosystem beträgt O (VM-Aufwand + Sprachimplementierungsaufwand), und beide Begriffe skalieren in gewisser Weise mit N = Anzahl der Sprachen. Sei M = Anzahl der Einbetter, k = Anzahl der Bindungen und a = durchschnittliche Anzahl von Bindungen, die eine bestimmte Sprache implementieren muss, mit a <= k. Zumindest haben wir M+N separate wasm-Implementierungen.
Ein naiver Ansatz, bei dem jede N Sprache unabhängig ABI FFI mit jeder anderen N Sprache implementiert, ist O(M + N*N). Dies ist, was wir auf nativen Systemen haben, was ein starkes Signal ist, dass jedes O(N*N) zu Ergebnissen führt, die sich nicht von nativen Systemen unterscheiden.
Zweiter naiver Ansatz, bei dem jede VM alle N*N-Bindungen implementieren muss: O(M*N*N + N), was eindeutig noch schlimmer ist.
Wir versuchen vorzuschlagen, dass wir k Bindungen haben, die zwischen einer abstrakten Sprache abbilden, die auf alle Sprachen zurückbilden. Dies impliziert k Arbeit für jede VM. Für jede Sprache müssen wir nur eine Teilmenge der Bindungen implementieren. Die Gesamtarbeit ist M*k + N*a, also O(M*k + N*k). Beachten Sie, dass für den Fall, dass k=N ist, die VM-Seite "nur" M*N ist, also für jede gegebene VM "nur" linear in der Anzahl der Sprachen ist. Natürlich wollen wir k << N, denn sonst ist dies immer noch O(N*N), nicht besser als die erste Lösung.
Dennoch ist O(M*k + N*k) viel schmackhafter. Wenn k gleich O(1) ist, macht dies das gesamte Ökosystem in der Anzahl der Implementierungen linear, was unsere untere Grenze für den damit verbundenen Aufwand darstellt. Eine wahrscheinlichere Schranke ist, dass k O(log(N)) ist, womit ich immer noch ziemlich zufrieden bin.

Das ist ein langer Weg zu sagen, ich bin völlig in Ordnung, die VM-Komplexität für diese Funktion um einen konstanten Faktor zu erhöhen.

Wir können einige Bindungen pro IR-Typ hinzufügen, um die überwiegende Mehrheit der Sprachen abzudecken.

Dies ist die entscheidende zugrunde liegende Annahme, von der ich glaube, dass sie einfach nicht wahr ist. Meine Erfahrung ist, dass es (mindestens!) so viele Darstellungsmöglichkeiten gibt wie es Sprachimplementierungen gibt. Und sie können beliebig kompliziert sein.

Nehmen wir V8, das allein einige Dutzend(!) Repräsentationen für Strings hat, einschließlich verschiedener Codierungen, heterogener Seile usw.

Der Fall Haskell ist weitaus komplizierter als Sie beschreiben, da Listen in Haskell faul sind, was bedeutet, dass Sie für jedes einzelne Zeichen in einer Zeichenfolge möglicherweise einen Thunk aufrufen müssen.

Andere Sprachen verwenden lustige Darstellungen für die Länge einer Zeichenfolge oder speichern sie nicht explizit, sondern erfordern eine Berechnung.

Diese beiden Beispiele zeigen bereits, dass ein deklaratives Datenlayout nicht ausreicht, Sie müssen oft in der Lage sein, Laufzeitcode aufzurufen, der wiederum seine eigenen Aufrufkonventionen haben kann.

Und das sind nur Strings, die konzeptionell ein recht einfacher Datentyp sind. Ich möchte gar nicht an die unendlichen Möglichkeiten denken, wie Sprachen Produkttypen (Tupel/Strukturen/Objekte) darstellen.

Und dann ist da die Empfangsseite, wo Sie all diese Datenstrukturen erstellen können müssen!

Daher halte ich es für völlig unrealistisch, dass wir jemals auch nur annähernd die "überwiegende Mehrheit der Sprachen" unterstützen würden. Stattdessen würden wir anfangen, einige zu privilegieren, während wir bereits einen großen Zoo mit willkürlichem Zeug anbauen. Das scheint auf mehreren Ebenen fatal.

Meine Erfahrung ist, dass es (mindestens!) so viele Darstellungsmöglichkeiten gibt wie es Sprachimplementierungen gibt. Und sie können beliebig kompliziert sein.

Ich stimme vollkommen zu. Ich denke, der Versuch, Typen zu entwerfen, die die internen Datendarstellungen der meisten Sprachen irgendwie abdecken, ist einfach nicht handhabbar und wird das Ökosystem übermäßig kompliziert machen.

Am Ende gibt es nur einen kleinsten gemeinsamen Nenner zwischen Sprachen, wenn es um Daten geht: den des "Puffers". Alle Sprachen können diese lesen und konstruieren. Sie sind effizient und einfach. Ja, sie bevorzugen Sprachen, die ihre Inhalte direkt ansprechen können, aber ich glaube nicht, dass dies eine Ungleichheit ist, die dadurch gelöst wird, dass (faule) Nachteile irgendwie auf das gleiche Maß an Unterstützung gebracht werden.

Tatsächlich können Sie mit nur einem einzigen Datentyp sehr weit kommen: dem Paar Zeiger + Linsen. Dann brauchen Sie nur ein "Schema", das sagt, was in diesen Bytes steht. Verspricht es, UTF-8-konform zu sein? Ist das letzte Byte garantiert immer 0? Sind die ersten 4/8 Bytes Längen-/Kapazitätsfelder? Sind all diese Bytes Little-Endian-Floats, die direkt an WebGL gesendet werden können? Sind diese Bytes vielleicht das Schema X eines vorhandenen Serialisierungsformats? etc?

Ich würde eine sehr einfache Schemaspezifikation vorschlagen, die all diese Fragen beantworten kann (kein vorhandenes Serialisierungsformat, sondern etwas niedrigeres, einfacheres und wasm-spezifisches). Es ist dann die Last jeder Sprache, diese Puffer im angegebenen Format effizient zu lesen und zu schreiben. Die dazwischen liegenden Ebenen können dann ohne Bearbeitung blind um die Puffer herumlaufen, entweder durch Kopieren oder, wenn möglich, durch Verweis/Ansicht.

Dies ist die entscheidende zugrunde liegende Annahme, von der ich glaube, dass sie einfach nicht wahr ist. Meine Erfahrung ist, dass es (mindestens!) so viele Darstellungsmöglichkeiten gibt wie es Sprachimplementierungen gibt. Und sie können beliebig kompliziert sein.

Ich stimme zu, dass dies die entscheidende zugrunde liegende Annahme ist. Ich bin nicht der Meinung, dass dies nicht wahr ist, obwohl ich denke, wegen einer semantischen Nuance, die ich nicht klar gemacht habe.

Die Bindungen sollen nicht alle Sprachdarstellungen perfekt abbilden, sie müssen nur alle Sprachen gut genug abbilden.

Das ist eine entscheidende zugrunde liegende Annahme, die dies unabhängig von der Codierung überhaupt handhabbar macht. Der Vorschlag von verlustbehaftete Codierung der Semantik eines bestimmten Programms handelt, von denen einige verlustbehafteter sind als andere.

Der Fall Haskell ist weitaus komplizierter als Sie beschreiben, da Listen in Haskell faul sind, was bedeutet, dass Sie für jedes einzelne Zeichen in einer Zeichenfolge möglicherweise einen Thunk aufrufen müssen.

Das hatte ich tatsächlich vergessen, aber ich denke, es spielt keine Rolle. Das Ziel besteht nicht darin, Haskell-Strings darzustellen, während alle ihre semantischen Nuancen über eine Modulgrenze hinweg erhalten bleiben. Das Ziel besteht darin, einen Haskell-String nach Wert in einen IR-String zu konvertieren. Dies beinhaltet notwendigerweise die Berechnung des gesamten Strings.

Diese beiden Beispiele zeigen bereits, dass ein deklaratives Datenlayout nicht ausreicht, Sie müssen oft in der Lage sein, Laufzeitcode aufzurufen, der wiederum seine eigenen Aufrufkonventionen haben kann.

Der Weg, dies zu modellieren, unabhängig davon, wie wir Bindungen angeben (oder sogar WENN wir etwas für Bindungen angeben), besteht darin, dies im Userland zu handhaben. Wenn die Darstellung eines Typs einer Sprache nicht direkt einer Bindung zugeordnet wird, muss sie in eine entsprechende Darstellung konvertiert werden. Wenn Haskells Strings beispielsweise wirklich als (type $haskellString (struct (field i8) (field (func (result (ref $haskellString)))))) , muss es entweder in einen strikten String konvertiert werden und eine Scheme-ähnliche Bindung verwenden, oder in ein Text-Array und eine Java-ähnliche Bindung verwenden, oder in a CFFIString und verwenden Sie eine C-ähnliche Bindung. Der Vorteil mehrerer unvollkommener Bindungstypen besteht darin, dass einige davon für Haskell weniger umständlich sind als andere, und es ist möglich, Wasm-FFI-Typen zu erstellen, ohne den Compiler ändern zu müssen.

Und das sind nur Strings, die konzeptionell ein recht einfacher Datentyp sind. Ich möchte gar nicht an die unendlichen Möglichkeiten denken, wie Sprachen Produkttypen (Tupel/Strukturen/Objekte) darstellen.
Und dann ist da die Empfangsseite, wo Sie all diese Datenstrukturen erstellen können müssen!

Ich bin verwirrt, ich sehe dies mit der Aussage "Das Binden zwischen Sprachen ist völlig unmöglich, also sollten wir es überhaupt nicht versuchen", aber ich glaube, das, was Sie gesagt haben, war eher in der Richtung "Ich glaube nicht, dass die der hier beschriebene Ansatz handhabbar ist", was viel vernünftiger erscheint. Insbesondere mein Einwand gegen diese Argumentation ist, dass sie keinen Weg nach vorne beschreibt. Angesichts der Tatsache, dass dieses Problem "sehr schwer" ist, was tun wir?

Stattdessen würden wir anfangen, ein paar zu privilegieren

Fast-sicher. Die Frage ist eine der, was ist das Ausmaß , in dem die wenigen optimal unterstützten Sprachen privilegiert sind? Wie viel Spielraum haben die benachteiligten Sprachen bei der Suche nach einer ergonomischen Lösung?

während bereits ein großer Zoo mit willkürlichem Zeug wächst.

Ich bin mir nicht sicher, was du damit meinst. Meine Interpretation dessen, was willkürlich ist, ist "Welche Sprachen unterstützen wir", aber das ist dasselbe wie "einige privilegieren", was doppelt zählen würde. Und somit wäre dies nur auf dieser einen Ebene fatal und nicht auf mehreren :D

@aardappel die kurze Version ist, dass dies mein Backup-Plan ist, wenn der deklarative abstrakte Ansatz fehlschlägt: Gehen Sie die völlig entgegengesetzte Richtung und beschreiben Sie ein Serialisierungsformat. Es wird beobachtet, dass das Web selbst fast ausschließlich auf Text aufbaut, da dieser ein extrem kleiner gemeinsamer Nenner ist. Text ist trivialerweise von allen Tools verständlich, und so ist so etwas wie das Web obendrein möglich.

Die größte Sorge, die ich mit Daten-in-Puffer habe, die diesen Ansatz unlösbar machen könnten, ist, wie wir mit Referenztypen umgehen können. Die beste Idee, die ich habe, ist, Tabellen freizugeben und Indizes in sie zu serialisieren, aber ich habe kein vollständiges Bild davon, wie gut das tatsächlich funktionieren würde.

@jgravelle-google vielleicht sollten Referenztypen getrennt gehalten werden? Eine Rohsignatur einer gegebenen Funktion könnte also ref ref i32 i32 i32 i32 was tatsächlich 2 Anyrefs gefolgt von 2 Puffern eines bestimmten Typs ist (angegeben im hypothetischen Schema oben).

(Nebenbei bemerkt, ich bin mit Haskell nicht vertraut, aber die Vorstellung von Strings als faule Listen von Zeichen haut mich um. Wann sind verknüpfte Bytelisten überhaupt der effizienteste oder bequemste Weg, etwas zu tun? unveränderlich sein und dass verknüpfte Listen ein billiges Voranstellen ermöglichen, aber Sie können unveränderliche Zeichenfolgen abrufen und bearbeiten, ohne verknüpfte Listen zu verwenden.)

Die größte Sorge, die ich mit Daten-in-Puffer habe, die diesen Ansatz unlösbar machen könnten, ist, wie wir mit Referenztypen umgehen können. Die beste Idee, die ich habe, ist, Tabellen freizugeben und Indizes in sie zu serialisieren, aber ich habe kein vollständiges Bild davon, wie gut das tatsächlich funktionieren würde.

Das ist einer der Gründe, warum ich capnproto als Codierung vorgeschlagen habe. Referenztabellen sind mehr oder weniger eingebaut.

In jedem Fall möchten wir, dass jedes von uns gewählte Format Referenztypen als erstklassige Bürger hat, die an einer beliebigen Stelle im Datendiagramm platziert werden können. (zB in Optionals, Arrays, Varianten usw.)

Vielen Dank an alle für Ihr Feedback.

Ich denke, wir fangen an, den Punkt zu erreichen, an dem wir größtenteils die gleichen Argumente mit sehr geringer Variation wiederholen, also werde ich den Vorschlag aktualisieren und versuchen, auf die Bedenken aller einzugehen. Ich werde wahrscheinlich mit einer neuen Ausgabe von vorne beginnen, sobald ich mit dem Schreiben des aktualisierten Vorschlags fertig bin.

Um das bisherige Feedback zusammenzufassen:

  • Es gibt sehr wenig Konsens darüber, welches Serialisierungsformat, wenn überhaupt, das beste für wasm wäre. Alternativen sind FlatBuffer, C ABI-Strukturgraphen mit Rohzeigern, ein maßgeschneidertes wasm-IDL-Format oder eine Kombination der oben genannten.

  • Der Vorschlag braucht einen stärkeren negativen Raum. Mehrere Leser waren verwirrt über den Umfang des Vorschlags und die Anwendungsfälle, die er erleichtern sollte (statische vs. dynamische Verknüpfung, Modul-zu-Host vs. Host-zu-Host, gemeinsame Nutzung veränderlicher Daten vs. Weitergabe unveränderlicher Nachrichten).

  • @lukewagner hat seine Begeisterung für das Potenzial eines Modulsystems geäußert, das sich gegenseitig misstrauende Module in Kombination mit der ESM-Integration verbindet. Die nächste Iteration des Vorschlags sollte dieses Potenzial erweitern; insbesondere glaube ich, dass ein abwärtskompatibles Typsystem es wasm ermöglichen würde, ein npm-ähnliches Abhängigkeitsbaummodell zu verwenden, während die Hauptlast des Abhängigkeitsrautenproblems vermieden wird.

  • Es gab wenig Feedback zum Thema Fähigkeiten, dh undurchsichtige Werte, die zurückgegeben und übergeben, aber nicht aus Rohdaten erstellt werden können. Ich nehme es als Zeichen dafür, dass die nächste Iteration viel mehr Gewicht auf sie legen sollte.

  • Mehrere Leser äußerten Bedenken hinsichtlich der Durchführbarkeit eines intersprachlichen Typensystems. Diese Bedenken sind etwas vage und schwer zu definieren, teilweise weil das Thema sehr abstrakt ist, teilweise weil der Vorschlag selbst ziemlich vage ist, was das Henne -Ei-Problem von

    • Sich zu sehr auf gut sichtbare Sprachen zu konzentrieren und mehr Nischensprachen hinter sich zu lassen.
    • Eine undichte Abstraktion zu haben, die versucht, zu allgemein zu sein, aber den Anwendungsfall von niemandem bequem oder effizient abdeckt.
    • Da zu viele Fälle abgedeckt werden, entsteht eine aufgeblähte N*N-Implementierung, die immer noch unter den oben genannten Problemen leidet.

Die nächste Vorschlagsiteration muss diese Bedenken auf die eine oder andere Weise angehen.

Insbesondere denke ich, dass die Diskussion von einigen Strohmann-Beispielen zum Nachdenken sehr profitieren würde. Diese Beispiele würden mindestens zwei Bibliotheken umfassen, die in verschiedenen Sprachen mit unterschiedlichen Datenlayouts geschrieben sind (zB C++ und Java), die von mindestens zwei Minimalprogrammen in verschiedenen Sprachen (zB Rust und Python) verwendet werden, um das n*n-Problem zu veranschaulichen und Strategien, damit umzugehen.

Außerdem verschränkt der Vorschlag, wie die Leser hervorgehoben haben, derzeit die Idee eines Typschemas mit der Idee seiner Repräsentation. Während der Vorschlag die Anforderungen eines Repräsentationsformats angemessen darlegt, muss er zunächst die Anforderungen eines abstrakten Typschemas festlegen.

Auf jeden Fall noch einmal vielen Dank an alle, die sich bisher an dieser Diskussion beteiligt haben. Ich werde versuchen, so schnell wie möglich einen genaueren Vorschlag zu unterbreiten. Wenn hier jemand daran interessiert ist, mir beim Schreiben zu helfen, schreibt mir eine E-Mail!

@jgravelle-google:

Der Weg, dies zu modellieren, unabhängig davon, wie wir Bindungen angeben (oder sogar WENN wir etwas für Bindungen angeben), besteht darin, dies im Userland zu handhaben.

Ja, ich stimme zu, und mein Argument ist dem von @aardappel ähnlich: Wenn wir das im Allgemeinen sowieso tun müssen, sollten wir das einfach akzeptieren und nicht ad-hoc Dinge versuchen, um einige seltsame Fälle zu verbessern. Im Userland gehört der Umbau hin, ganz im Sinne von Wasm.

Ich bin verwirrt, ich sehe das so, als würde ich sagen "das Binden zwischen Sprachen ist völlig unmöglich, also sollten wir es überhaupt nicht versuchen",

Ich denke, es ist absolut wünschenswert, ein DDL (Typenschema) für die Dateninterop zwischen Sprachen zu definieren. Ich glaube einfach nicht, dass es gangbar ist, Konvertierungen in Wasm zu bauen. Die Konvertierungen müssen im Userland implementiert werden. Die Bindeschicht schreibt lediglich ein Format vor, das der Benutzercode erzeugen/verbrauchen muss.

während bereits ein großer Zoo mit willkürlichem Zeug wächst.
Ich bin mir nicht sicher, was du damit meinst. Meine Interpretation dessen, was willkürlich ist, ist "Welche Sprachen unterstützen wir", aber das ist dasselbe wie "einige privilegieren", was doppelt zählen würde.

Entschuldigung, ich meinte, ich vermute, dass an diesen Konvertierungen nichts schrecklich Kanonisches sein wird. Sowohl ihre Auswahl ist also "willkürlich" als auch ihre individuelle Semantik.

Wie können wir mit Referenztypen umgehen?

Ach, das ist eine gute Frage. FWIW, wir versuchen gerade dieses Problem für eine IDL/DDL für die Dfinity-Plattform anzugehen. Solange es nur anyref gibt, ist die Lösung ziemlich einfach: Das Serialisierungsformat definiert zwei Teile, einen Speicher-Slice, der die transparenten Daten projiziert, und einen Tabellen-Slice, der die enthaltenen Referenzen projiziert. Mehrere Referenztypen erfordern dementsprechend mehrere Tabellen-Slices. Die knifflige Frage ist, was zu tun ist, wenn die Menge der Ref-Typen nicht mehr endlich ist (zB mit typisierten Funktionsreferenzen).

Sobald wir GC-Typen haben, sollte es eine alternative Möglichkeit geben, die Daten bereitzustellen, nämlich als GC-Wert. Referenzen sind dann kein Thema, da sie frei gemischt werden können.

@PoignardAzur :

Abgesehen davon bin ich mit Haskell nicht vertraut, aber die Vorstellung von String als faule Listen von Zeichen haut mich um.

Ja, ich glaube, es wird heutzutage allgemein als Fehler angesehen. Aber es zeigt, wie viel Vielfalt es selbst bei „einfachen“ Datentypen gibt.

@rossberg

Ich denke, es ist absolut wünschenswert, ein DDL (Typenschema) für die Dateninterop zwischen Sprachen zu definieren. Ich glaube einfach nicht, dass es gangbar ist, Konvertierungen in Wasm zu bauen. Die Konvertierungen müssen im Userland implementiert werden.

Ich stimme zu, und um dem hinzuzufügen: Ich bin skeptisch, der wasm-Spezifikation dafür etwas hinzuzufügen, weil ich nicht glaube, dass wasm einen größeren Bedarf an einer intersprachlichen Lösung hat als andere Plattformen, und ich glaube nicht, dass wasm hat eine größere Fähigkeit , eine solche Lösung zu implementieren als andere Plattformen. Wasm hier ist für mich offensichtlich nichts Besonderes, und daher bin ich mir nicht sicher, warum wir in diesem Bereich besser abschneiden können als Standardlösungen, z. B. Puffer wie

Die Besonderheit von wasm, zumindest im Web, sind die JavaScript/Web-API-Typen für Strings und Arrays und so weiter. Es ist natürlich wichtig, mit ihnen interagieren zu können.

Ich glaube nicht, dass wasm einen größeren Bedarf an einer intersprachlichen Lösung hat als andere Plattformen

Ich denke, es geht. Die standardmäßige Verwendung im Web bedeutet, dass Code in verschiedenen Kontexten ausgeführt werden kann und wird. Genauso wie man <script src="http://some.other.site/jquery.js"> , würde ich es lieben, wenn Leute wasm-Bibliotheken ursprungsübergreifend kombinieren würden. Aufgrund der Kurzlebigkeits- und Zusammensetzbarkeitseigenschaften, die das Web bietet, ist der Mehrwert, mit einem fremden Modul verbunden zu werden, höher als je zuvor auf nativen Systemen.

und ich glaube nicht, dass wasm eine größere Fähigkeit hat, eine solche Lösung zu implementieren als andere Plattformen.

Und ich denke, das tut es. Da wasm von einem Embedder / in einem Host ausgeführt wird, wird die Codegenerierung effektiv abstrahiert. Aus diesem Grund hat eine VM viel mehr Tools + Spielraum, um übergeordnete Konstrukte zu unterstützen, die auf nativen Systemen nicht möglich sind.

Ich denke also, dass etwas in diesem Bereich wertvoller und möglicher ist als auf anderen Systemen, deshalb ist wasm in diesem Zusammenhang etwas Besonderes. Für mich ist der JS-Interop ein Sonderfall der allgemeineren Vorstellung, dass wasm-Module in der Lage sein müssen, mit externen Dingen mit sehr unterschiedlichen Ansichten der Welt zu sprechen.

Ein Weg nach vorne besteht darin, dies vorerst vollständig in die Interoperabilität auf Tool-Ebene zu verlagern und die Standardisierung aufzuschieben, bis wir ein siegreiches Format haben. Wenn also das Ziel darin besteht, dass das Ökosystem des vorherrschenden wasm-Paketmanagers ein bestimmtes Schnittstellenformat verwendet (und ist das NPM oder WAPM oder ein noch nicht erstellter Paketmanager?), dann kann dies unabhängig von der Standardisierung geschehen. Theoretisch können wir standardisieren, was die Leute bereits tun, um eine bessere Leistung zu erzielen, aber die Ergonomie kann im Benutzerland implementiert werden. Ein Risiko besteht darin, dass sich das siegreiche Interlanguage-Format nicht für eine Optimierung eignet und wir mit einem suboptimalen De-facto-Standard enden. Wenn wir ein Format mit der Absicht entwerfen können, später zu standardisieren (deklarativer Stil in einem benutzerdefinierten Abschnitt ist meistens ausreichend?), das dieses Risiko beseitigt, aber auch Leistungsverbesserungen verzögert. Für mich ist Leistung einer der weniger aufregenden Motivatoren, um so etwas zu haben, also bin ich damit ziemlich einverstanden, obwohl andere möglicherweise anderer Meinung sind.

(und ist das NPM oder WAPM oder ein noch nicht erstellter Paketmanager?)

Ich denke, es ist viel zu früh für WAPM, ein brauchbarer Paketmanager zu sein. Wir brauchen Funktionen wie ESM-Integration, WASI und irgendeine Form von Sprachbindungen, die standardisiert werden, bevor ein wasm-Paketmanager machbar wird.

So wie es ist, glaube ich, dass WAPM nicht einmal über ein Abhängigkeitsmanagement verfügt.

Ich glaube nicht, dass wasm einen größeren Bedarf an einer intersprachlichen Lösung hat als andere Plattformen

Ich denke, es geht. Die standardmäßige Verwendung im Web bedeutet, dass Code in verschiedenen Kontexten ausgeführt werden kann und wird. So wie man es könnte

Die Konvertierungen müssen im Userland implementiert werden. Die Bindeschicht schreibt lediglich ein Format vor, das der Benutzercode erzeugen/verbrauchen muss.

Das ist eine gute Zusammenfassung für mich.

Während ich an einem neuen Entwurf schreibe, habe ich eine offene Frage an alle in diesem Thread:

Gibt es eine vorhandene Bibliothek, die Sie, wenn sie zu WebAssembly kompiliert würde, in jeder Sprache verwenden möchten?

Ich suche im Wesentlichen nach potenziellen Anwendungsfällen, auf denen das Design basieren kann. Ich habe meine eigenen (insbesondere React, die Bullet-Engine und Plugin-Systeme), aber ich hätte gerne mehr Beispiele, mit denen ich arbeiten kann.

@PoignardAzur Viele Sprachen in C verwenden die gleichen Perl-kompatiblen regulären Ausdrücke (PCRE)-Bibliotheken, aber in der Browsereinbettung sollten sie wahrscheinlich die Regex-API von JS verwenden.

@PoignardAzur BoringSSL und libsodium fallen mir ein.

Auch die Cap'n Proto RPC-Implementierung, aber dies ist eine seltsame: Die _Serialization_-Schicht von Cap'n Proto muss realistischerweise in jeder Sprache unabhängig implementiert werden, da das meiste eine breite, aber flache API-Schicht ist, die idiomatisch und inline sein muss -freundlich. Die RPC-Schicht, OTOH, ist schmal, aber tief. Im Prinzip sollte es möglich sein, die C++-RPC-Implementierung hinter der Serialisierungsimplementierung jeder beliebigen Sprache zu verwenden, indem capnp-codierte Byte-Array-Referenzen über die FFI-Grenze übergeben werden...

Ich denke, das zu tun, was letztendlich vorgeschlagen wird, würde einige ziemlich invasive Änderungen an der Web Assembly selbst erfordern, da sie bereits existiert - aber es könnte sich wohl lohnen.

Ich möchte anmerken, dass die SmallTalk-Welt einige positive Erfahrungen mit solchen Bemühungen gemacht hat, die bei der Entwicklung ihres State Replication Protocol (SRP) aufschlussreich sein könnten, einem effizienten Serialisierungsprotokoll, das jeden Typ jeder Größe ziemlich effizient darstellen kann. Ich habe darüber nachgedacht, es zu einem nativen Speicherlayout für eine VM oder sogar FPGA zu machen, bin aber nicht dazu gekommen, es auszuprobieren. Ich weiß, dass es mit guten Ergebnissen auf mindestens eine andere Sprache, Squeak, portiert wurde. Sicherlich etwas zum Nachlesen, da es starke Überschneidungen mit den Themen, Herausforderungen und Erfahrungen dieses Vorschlags gibt.

Ich verstehe, warum Web IDL der Standardvorschlag als verbindliche Sprache war: Es ist die historische und irgendwie ausgereifte verbindliche Sprache für das Web. Ich unterstütze diese Entscheidung sehr, und es ist sehr wahrscheinlich, dass ich dasselbe getroffen hätte. Trotzdem können wir erkennen, dass es kaum in andere Kontexte passt (verstehen, andere Hosts/Sprachen). Wasm ist host-agnostisch oder sprach-/plattform-agnostisch. Ich mag die Idee, ausgereifte Web-Technologien zu verwenden und einen Anwendungsfall für Nicht-Web-Szenarien zu finden, aber im Fall von Web IDL scheint es wirklich mit dem Web verbunden zu sein. Aus diesem Grund verfolge ich diese Gespräche hier sehr genau.

Ich habe https://github.com/WebAssembly/webidl-bindings/issues/40 geöffnet, was mich dazu veranlasste, hier eine Frage zu stellen, da ich keine Erwähnung gesehen habe (oder ich habe sie übersehen).

In der ganzen Bindungsgeschichte ist nicht klar, „wer“ für die Erstellung der Bindungen verantwortlich ist:

  • Ist es ein Compiler (der ein Programm in ein Wasm-Modul umwandelt)?
  • Ist es ein Programmautor (und sind die Bindungen handgeschrieben)?

Ich denke, beides ist gültig. Und im Fall von Web IDL scheint es einige Einschränkungen zu zeigen (siehe Link oben). Vielleicht habe ich nur einen wichtigen Schritt im Prozess übersehen und denke daher daran, meine Nachricht zu vergessen.

Auch wenn das Ziel darin besteht, Web IDL „neu auszurichten“, um weniger Web-zentriert zu sein, ist es im Moment _sehr Web-zentriert. Und es tauchen Vorschläge auf, Alternativen vorzuschlagen, daher dieser Thread. Daher befürchte ich eine mögliche Fragmentierung. Im Idealfall (und so war Wasm bisher konzipiert) ist ein Wasm-Modul inklusive seiner Bindings überall lauffähig. Mit Bindings, die in Web IDL, Cap'n'Proto, FlatBuffers, was auch immer, geschrieben sind, bin ich mir ziemlich sicher, dass nicht alle Compiler oder Programmautoren dieselben Bindings in unterschiedlichen Syntaxen schreiben werden, um wirklich plattformübergreifend zu sein. Das ist witzigerweise ein Argument für handgeschriebene Bindungen: Leute können zu einem Programm beitragen, indem sie Bindungen für die Plattform P schreiben. Aber geben wir zu, dass dies nicht ideal ist.

Um es zusammenzufassen: Ich bin besorgt über eine mögliche Fragmentierung zwischen Web- und Nicht-Web-Bindungen. Wenn eine Nicht-Webbindungssprache verwendet wird, würde sie realistischerweise von Webbrowsern implementiert? Sie müssten Bindings schreiben „Wasm ⟶ Binding Language B ⟶ Web IDL“. Beachten Sie, dass dies für alle Hosts dasselbe Szenario ist: Wasm ⟶ Binding Language B ⟶ Host-API.

Für die Neugierigen arbeite ich bei Wasmer und bin Autor der PHP- , Python- , Ruby- und Go- Wasm-Integrationen. Wir haben einen schönen Spielplatz, um verschiedene Bindungen für sehr unterschiedliche Hosts zu hacken. Wenn jemand möchte, dass ich verschiedene Lösungen integriere, Feedback sammle oder experimentiere, sind wir alle offen und bereit, zusammenzuarbeiten und mehr Ressourcen dafür bereitzustellen.

Die aktuelle Richtung bei 'webIDL-Bindungen' geht wahrscheinlich weg von webIDL
selbst. Das Dilemma ist jedoch folgendes:

Die 'natürliche' Sprache zum Ausdrücken von Inter-Modul und Modul-Host
Interoperabilität ist wesentlich reichhaltiger als die natürliche Sprache von WASM. Dies
bedeutet, dass jedes verwendbare IDL-Äquivalent ziemlich willkürlich aussieht
WASMs Standpunkt.

Auf der anderen Seite für Leute, die die Welt aus der Sicht von C/C++ sehen
(und Rust und seinesgleichen) alles, was reicher ist als das Modell von WASM, riskiert,
unbrauchbar. Wir sehen dies bereits an der Schwierigkeit der Integration von ref
Typen in die Werkzeugkette.

Darüber hinaus ist es kein direktes Mandat von WASM, allgemeine
intersprachliche Interoperabilität. IMO sollte es auch nicht sein.

(Es gibt eine eingeschränktere Version der Inter-Sprache-Interoperabilität, die ich
glauben ist nicht nur unterstützend, sondern auch kritisch: es ist in all unseren
Interesse, das Anbieter von Fähigkeiten und Nutzer von Fähigkeiten erfüllen können
mit minimaler Reibung. (Fähigkeit ist Managementsprache, aber ich habe
keinen besseren Begriff gefunden.) Dies erfordert einen anderen IDL-Stil und einen
das ist einfacher zu implementieren, als es für eine vollständige Inter-Sprache erforderlich wäre
Interoperabilität.)

Fazit: Es gibt Argumente für ein IDL-Äquivalent, und wir brauchen es
um die Zusammenarbeit über Eigentumsgrenzen hinweg zu unterstützen. Was am Ende steht
Sein ist im Moment nicht klar.

Am Mo, 24.06.2019 um 07:02 Ivan Enderlin [email protected]
schrieb:

Ich verstehe, warum Web IDL als verbindliche Sprache der Standardvorschlag war:
Es ist die historische und irgendwie ausgereifte verbindliche Sprache für das Web. ich
unterstütze diese Entscheidung sehr, und es ist sehr wahrscheinlich, dass ich die
gleich. Nichtsdestotrotz können wir erkennen, dass es kaum in andere Kontexte passt
(verstehen, andere Hosts/Sprachen). Wasm ist host-agnostisch,
oder sprach-/plattformunabhängig. Ich mag die Idee, ein ausgereiftes Web zu verwenden
Technologien und um einen Anwendungsfall für Nicht-Web-Szenarien zu finden, aber in dem Fall
von Web IDL scheint es wirklich mit dem Web verbunden zu sein. Aus diesem Grund bin ich sehr
Verfolge diese Gespräche hier genau.

Ich habe WebAssembly/webidl-bindings#40 geöffnet
https://github.com/WebAssembly/webidl-bindings/issues/40 , was mich geführt hat
um hier eine Frage zu stellen, da ich keine Erwähnung gesehen habe (oder ich habe sie übersehen).

In der ganzen Bindungsgeschichte ist nicht klar, „wer“ dafür verantwortlich ist
Generieren Sie die Bindungen:

  • Ist es ein Compiler (der ein Programm in ein Wasm-Modul umwandelt)?
  • Ist es ein Programmautor (und sind die Bindungen handgeschrieben)?

Ich denke, beides ist gültig. Und im Fall von Web IDL scheint es einige zu zeigen
Einschränkungen (siehe Link oben). Vielleicht habe ich nur einen wichtigen Schritt verpasst
den Prozess, und denken Sie daher daran, meine Nachricht zu vergessen.

Auch wenn das Ziel darin besteht, Web IDL „neu auszurichten“, um weniger Web-zentriert zu sein,
es ist sehr Web-zentriert. Und es entstehen Vorschläge, um Alternativen vorzuschlagen,
daher dieser Thread. Daher befürchte ich eine mögliche Fragmentierung.
Idealerweise (und so wurde Wasm bisher konzipiert) mit einem Wasm-Modul
einschließlich seiner Bindungen ist es möglich, es überall so auszuführen, wie es ist. Mit
Bindings geschrieben in Web IDL, Cap'n'Proto, FlatBuffers, was auch immer, ich bin
ziemlich sicher werden nicht alle Compiler oder Programmautoren dasselbe schreiben
Bindungen in verschiedenen Syntaxen, um wirklich plattformübergreifend zu sein. Komischerweise ist das
ein Argument für handschriftliche Bindungen: Menschen können zu einem
Programm, indem Sie Bindungen für Plattform P schreiben. Aber geben wir zu, dass dies nicht der Fall sein wird
überhaupt ideal.

Um es zusammenzufassen: Ich bin besorgt über eine mögliche Fragmentierung zwischen Web und
Nicht-Web-Bindungen. Wenn eine nicht webbindende Sprache verwendet wird, wäre dies
von Webbrowsern realistisch umgesetzt? Sie müssten schreiben
Bindungen „Wasm ⟶ Bindungssprache B ⟶ Web IDL“. Beachten Sie, dass dies dasselbe ist
Szenario für alle Hosts: Wasm ⟶ Binding Language B ⟶ Host-API.

Für Neugierige arbeite ich bei Wasmer und bin Autor des PHP-
https://github.com/wasmerio/php-ext-wasm , Python-
https://github.com/wasmerio/python-ext-wasm , Ruby-
https://github.com/wasmerio/ruby-ext-wasm und Go-
https://github.com/wasmerio/go-ext-wasm Wasm-Integrationen. Wir starten
einen schönen Spielplatz haben, um verschiedene Bindungen für sehr unterschiedliche zu hacken
Gastgeber. Wenn jemand möchte, dass ich verschiedene Lösungen integriere, um zu sammeln
Feedback, oder versuchen Sie zu experimentieren, wir sind alle offen und bereit zusammenzuarbeiten
und mehr Ressourcen darauf legen.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUD6WA22DDUS7PYQ6F3P4DHYRA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVX50Zment2
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAQAXUGM66AWN7ZCIVBTXVTP4DHYRANCNFSM4HJUHVGQ
.

--
Francis McCabe
SW

Wir sehen dies bereits an der Schwierigkeit, Ref-Typen in die Werkzeugkette zu integrieren.

Ich kann nicht für andere Sprachen sprechen, aber Rust + wasm-bindgen unterstützt bereits:

Da bin ich gespannt: Welche Schwierigkeiten meinen Sie?

Mein Verständnis der Schwierigkeiten liegt eher am C++-Ende. Rust verfügt über ausreichend leistungsfähige Metaprogrammierung, um dies auf der Sprachseite vernünftiger zu machen, aber Userland C++ hat es beispielsweise schwerer, über Anyrefs nachzudenken.

Ich wäre neugierig, hier mehr über die spezifischen Probleme von C++ zu erfahren. (Sind sie C++-spezifisch oder LLVM-spezifisch?)

C++ weiß nicht, was ein ref-Typ ist. Sie können es also nicht in sich haben
beliebiges Objekt. Nicht wirklich Teil der Sprache; eher wie eine Datei
Beschreibung. Lustiger Platz für eine Saite.

Am Montag, 24. Juni 2019 um 15:07 Uhr schrieb Alon Zakai [email protected] :

Ich wäre neugierig, hier mehr über die spezifischen Probleme von C++ zu erfahren. (Sind sie
C++-spezifisch oder LLVM-spezifisch?)


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/WebAssembly/design/issues/1274?email_source=notifications&email_token=AAQAXUDW237MUBBUUJLKS6LP4FARJA5CNFSM4HJUHVG2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63DNMV2HOLD
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAQAXUB4O3ZX4LRQSQRL763P4FARJANCNFSM4HJUHVGQ
.

--
Francis McCabe
SW

Wenn man offline mit @fgmccabe spricht , stimmt es, dass sowohl C++ als auch Rust einen Ref-Typ nicht direkt in einer Struktur speichern können, da die Struktur im linearen Speicher gespeichert wird. Sowohl C++ als auch Rust können natürlich mit Ref-Typen indirekt umgehen, genauso wie sie mit Dateideskriptoren, OpenGL-Texturen usw. umgehen - mit Integer-Handles. Ich denke, sein Punkt ist, dass keine dieser beiden Sprachen "gut" / "nativ" mit Ref-Typen umgehen kann (korrigiere mich, wenn ich falsch liege!), dem ich zustimme - diese Sprachen werden beim Ref-Typ immer im Nachteil sein Operationen im Vergleich zu GC-Sprachen.

Ich bleibe gespannt, ob es hier etwas Spezifisches für C++ gibt. Ich glaube nicht, dass es das gibt?

Mein Verständnis dafür, was C++ hier schwierig macht, ist, wenn Sie sagen:

struct Anyref; // opaque declaration
void console_log(Anyref* item); // declaration of ref-taking external JS API
Anyref* document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref* elem = document_getElementById("foo");
  console_log(elem);
}

void notSoWellBehaved() {
  // ????
  Anyref* elem = document_getElementById("bar");
  Anyref* what = (Anyref*)((unsigned int)elem + 1);
  console_log(what);
}

Die gute Nachricht ist, dass das letztere Beispiel meiner Meinung nach UB ist (ungültige Zeiger sind UB, sobald sie erstellt wurden), aber wie versuchen wir, das in LLVM IR zu modellieren?

@jgravelle-google Ich denke, sogar struct Anyref; setzt voraus, dass es im linearen Speicher Sinn macht. Warum modellieren Sie es stattdessen nicht mit einem Integer-Handle, wie bereits erwähnt, als OpenGL-Texturen, Datei-Handles und so weiter?

using Anyref = uint32_t; // handle declaration
void console_log(Anyref item); // declaration of ref-taking external JS API
Anyref document_getElementById(const char* str);

void wellBehaved() {
  // This should work
  Anyref elem = document_getElementById("foo");
  console_log(elem);
}

Das Integer-Handle muss in der Tabelle nachgeschlagen werden, wenn es verwendet werden soll - auch dies ist nur ein Nachteil von Sprachen, die linearen Speicher wie C++ und Rust verwenden. Es könnte aber durchaus zumindest lokal optimiert werden – wenn nicht durch LLVM, dann auf wasm-Ebene.

Das wird funktionieren, aber dann müssen Sie sicherstellen, dass table_free(elem) aufgerufen wird, oder Sie behalten für immer eine Referenz darauf. Was für C++ nicht seltsam ist, zugegeben.

Es ist ein seltsames Mapping, weil es meiner Meinung nach nicht gut geschichtet ist? Es fühlt sich an wie eine Bibliothek a la OpenGL, aber es ist auf Compiler-Magie angewiesen - ich glaube nicht, dass man in C++ ein anyref.h bauen kann, selbst mit einem Inline-Wasm, wenn man darauf angewiesen ist, ein separates zu deklarieren Tisch.

Wie auch immer, ich denke, es ist alles machbar / handhabbar, aber nicht einfach ist alles.

@kripken Es stimmt, dass "richtige" native anyref Unterstützung einige Änderungen an LLVM (und in rustc) erfordert, aber das ist eigentlich kein Hindernis.

wasm-bindgen speichert echte wasm anyref s in einer wasm-Tabelle und speichert dann im linearen Speicher einen ganzzahligen Index in die Tabelle. So kann es dann auf anyref zugreifen, indem es die Anweisung wasm table.get .

Bis wasm-gc implementiert ist, müssen GC-Sprachen genau dieselbe Strategie verwenden, damit Rust (et al.) nicht zu kurz kommt.

Was würde uns also die native anyref Unterstützung in LLVM bringen? Nun, es würde es ermöglichen, anyref direkt von Funktionen zu übergeben/zurückzugeben, anstatt anyref indirekt durch eine wasm-Tabelle zu führen. Das wäre nützlich, ja, aber das ist nur eine Leistungsoptimierung, die die Verwendung von anyref nicht wirklich verhindert.

@Pauan

wasm-bindgen speichert echte wasm-Anyrefs in einer wasm-Tabelle und speichert dann im linearen Speicher einen ganzzahligen Index in die Tabelle. So kann es dann auf die Anyref zugreifen, indem es die Anweisung wasm table.get verwendet.

Genau, ja, das ist das Modell, auf das ich mich bezog.

Bis wasm-gc implementiert ist, müssen GC-Sprachen genau dieselbe Strategie verwenden, damit Rust (et al.) nicht zu kurz kommt.

Ja, im Moment haben GC-Sprachen keinen Vorteil, weil wir kein natives wasm-GC haben. Aber das ändert sich hoffentlich! :) Letztendlich erwarte ich, dass GC-Sprachen hier einen klaren Vorteil haben, zumindest wenn wir GC richtig machen.

Was würde uns also die native Anyref-Unterstützung in LLVM bringen? Nun, es würde die direkte Übergabe/Rückgabe von Anyref von Funktionen ermöglichen, anstatt die Anyref über eine wasm-Tabelle indirekt zu führen. Das wäre nützlich, ja, aber das ist nur eine Leistungsoptimierung, die die Verwendung von anyref nicht wirklich verhindert.

Zugegeben, ja, dies wird nur ein Vorteil von GC-Sprachen (eventuell) gegenüber C++ und Rust usw. sein. Es verhindert nicht die Verwendung.

Zyklen sind jedoch ein größeres Problem für C++ und Rust, da Tabellen root sind. Vielleicht können wir eine Ablaufverfolgungs-API oder "Schattenobjekte" haben, im Grunde eine Möglichkeit, die Struktur von GC-Links in C++/Rust abzubilden, damit die externe GC sie verstehen kann. Aber ich glaube nicht, dass es dafür noch einen konkreten Vorschlag gibt.

Letztendlich erwarte ich, dass GC-Sprachen hier einen klaren Vorteil haben, zumindest wenn wir GC richtig machen.

Ich könnte mich irren, aber es würde mich wundern, wenn das der Fall wäre: GC-Sprachen müssten eine wasm-GC-Struktur zuweisen und dann müsste die wasm-Engine dies verfolgen, während sie durch das Programm fließt.

Im Vergleich dazu benötigt Rust keine Zuweisung (nur einer Tabelle zuweisen) und muss nur eine ganze Zahl speichern, und die Wasm-Engine muss für GC-Zwecke nur eine statische, sich nicht bewegende Tabelle verfolgen.

Ich nehme an, es ist möglich, dass anyref Zugriff für GC-Sprachen optimiert werden kann, da er nicht table.get müsste, aber ich erwarte, dass table.get ziemlich schnell ist.

Könnten Sie also mehr darüber erklären, wie Sie erwarten, dass ein wasm-gc-Programm besser abschneidet als ein Programm, das eine wasm-Tabelle verwendet?

PS Das gerät langsam vom Thema ab, also sollten wir diese Diskussion vielleicht in einen neuen Thread verschieben?

Eigentlich nur: table.get/table.set vermeiden. Bei GC sollten Sie den Rohzeiger genau dort haben und die Umleitungen speichern. Aber ja, Sie haben Recht, dass Rust und C++ nur eine Ganzzahl speichern müssen und sie insgesamt ziemlich schnell sind, sodass jeder GC-Vorteil möglicherweise keine Rolle spielt!

Ich stimme zu, dass wir vielleicht vom Thema abkommen, ja. Ich denke, was Thema ist, ist der Punkt von @fgmccabe , dass Ref-Typen nicht so natürlich in lineare speicherverwendende Sprachen passen. Das kann uns bei den Bindings in gewisser Weise beeinflussen (insbesondere Zyklen sind besorgniserregend, weil C++ und Rust nicht damit umgehen können, aber vielleicht können die Bindings das ignorieren?), also nur etwas, mit dem ich vorsichtig sein sollte - beides, um es zu versuchen dafür sorgen, dass die Dinge für so viele Sprachen wie möglich funktionieren und nicht übermäßig von den Einschränkungen einer bestimmten Sprache beeinflusst werden.

@kentonv

Die Serialisierungsschicht von Cap'n Proto muss realistischerweise in jeder Sprache unabhängig implementiert werden, da das meiste eine breite, aber flache API-Schicht ist, die idiomatisch und inline-freundlich sein muss. Die RPC-Schicht, OTOH, ist schmal, aber tief

Welcher Ordner ist das?

@PoignardAzur Entschuldigung, ich verstehe Ihre Frage nicht.

@kentonv Ich

@PoignardAzur Also, das kommt auf meinen Punkt zurück. Es gibt nicht wirklich eine einzige Stelle, auf die Sie zeigen und sagen können "das ist die Serialisierungsschicht". Meistens ist die "Serialisierung" von Cap'n Proto nur eine Zeigerarithmetik um Laden/Speichern auf einen zugrunde liegenden Puffer. Bei einer Schemadatei verwenden Sie den Codegenerator, um eine Headerdatei zu generieren, die Inline-Methoden definiert, die die richtige Zeigerarithmetik für die bestimmten im Schema definierten Felder ausführen. Der Anwendungscode muss diese generierten Accessoren dann jedes Mal aufrufen, wenn er ein Feld liest oder schreibt.

Aus diesem Grund wäre es nicht sinnvoll, eine Implementierung aufzurufen, die in einer anderen Sprache geschrieben ist. Die Verwendung einer rohen FFI für jeden Feldzugriff wäre äußerst umständlich, sodass Sie zweifellos einen Codegenerator schreiben würden, der die FFI in etwas Hübscheres (und spezifischeres für Ihr Schema) verpackt. Aber dieser generierte Code wäre mindestens so kompliziert wie der Code, den Cap'n Proto bereits implementiert – wahrscheinlich komplizierter (und viel langsamer!). Es ist also sinnvoller, direkt einen Codegenerator für die Zielsprache zu schreiben.

Es gibt vielleicht einige interne Hilfsfunktionen innerhalb der Cap'n Proto-Implementierung, die geteilt werden könnten. Genauer gesagt enthält layout.c++ / layout.h gesamten Code, der die Zeigerkodierung von capnp interpretiert, die Begrenzungsprüfung durchführt usw. Die generierten Code-Zugriffsmethoden rufen diesen Code beim Lesen/Schreiben von Zeigerfeldern auf. Ich könnte mir also vielleicht vorstellen, diesen Teil in ein FFI einzupacken, um aus mehreren Sprachen aufgerufen zu werden; aber ich würde immer noch erwarten, Codegeneratoren und eine gewisse Menge an Laufzeitunterstützungsbibliothek in jeder Zielsprache zu schreiben.

Ja, sorry, ich meinte das Gegenteil ^^ (die RPC-Schicht)

@PoignardAzur Ohhh, und ich nehme an, Sie sind speziell daran interessiert, sich die Schnittstellen anzusehen, da Sie darüber nachdenken, wie Sie sie in ein FFI einpacken. Dann willst du also:

  • capability.h : Abstrakte Schnittstellen zur Darstellung von Fähigkeiten und RPC-Aufrufen, die theoretisch durch eine Vielzahl von Implementierungen unterstützt werden könnten. (Dies ist der wichtigste Teil.)
  • rpc.h : Implementierung von RPC über ein Netzwerk.
  • rpc-twoparty.h : Transportadapter für RPC über eine einfache Verbindung.

Dieser Vorschlag wird jetzt durch Nr. 1291 ersetzt: OCAP-Bindungen.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

frehberg picture frehberg  ·  6Kommentare

artem-v-shamsutdinov picture artem-v-shamsutdinov  ·  6Kommentare

mfateev picture mfateev  ·  5Kommentare

nikhedonia picture nikhedonia  ·  7Kommentare

cretz picture cretz  ·  5Kommentare