Übertragbare Objekte – blitzschnell

In Chrome 13 wurde das Senden von ArrayBuffers an/von einem Webworker mithilfe eines Algorithmus namens strukturiertes Klonen eingeführt. So konnten über die postMessage() API nicht nur Strings, sondern auch komplexe Typen wie File, Blob, ArrayBuffer und JSON-Objekte akzeptiert werden. Das strukturierte Klonen wird auch in späteren Versionen von Firefox unterstützt.

Schneller ist besser

Das strukturierte Klonen ist zwar eine gute Sache, aber es handelt sich dabei immer noch um einen Kopiervorgang. Der Overhead beim Übergeben einer 32-MB-ArrayBuffer an einen Worker kann mehrere hundert Millisekunden betragen. Neue Browserversionen bieten eine enorme Leistungssteigerung bei der Nachrichtenweitergabe, die übertragbare Objekte genannt wird.

Bei übertragbaren Objekten werden Daten von einem Kontext in einen anderen übertragen. Es ist ein Zero-Copy-Verfahren, wodurch die Leistung beim Senden von Daten an einen Worker erheblich verbessert wird. Wenn Sie mit C/C++ vertraut sind, können Sie sich das als „Pass-by-Reference“ vorstellen. Im Gegensatz zur Weitergabe durch Verweis ist die „Version“ aus dem Aufrufkontext nach der Übertragung in den neuen Kontext nicht mehr verfügbar. Wenn Sie beispielsweise eine ArrayBuffer aus Ihrer Haupt-App an den Worker übertragen, wird die ursprüngliche ArrayBuffer gelöscht und kann nicht mehr verwendet werden. Der Inhalt wird (ziemlich wörtlich) in den Worker-Kontext übertragen.

Für die Verwendung übertragbarer Inhalte gibt es eine neue Version von postMessage(), die diese unterstützt:

worker.postMessage(arrayBuffer, [transferableList]);
window.postMessage(arrayBuffer, targetOrigin, [transferableList]);

Im Fall des Workers ist das erste Argument die ArrayBuffer-Nachricht. Das zweite Argument ist eine Liste der Elemente, die übertragen werden sollen. In diesem Beispiel geben Sie die arrayBuffer in der Liste der übertragbaren Elemente an.

Benchmark-Demo

Um die Leistungssteigerungen durch Übertragbare zu sehen, habe ich eine Demo zusammengestellt.

In der Demo wird eine 32 MB große ArrayBuffer mit postMessage() an einen Worker und zurückgesendet. Wenn Ihr Browser keine übertragbaren Elemente unterstützt, wird das Sample auf das strukturierte Klonen zurückgesetzt. Bei durchschnittlich fünf Durchläufen in verschiedenen Browsern habe ich Folgendes festgestellt:

Vergleichsdiagramm: Strukturiertes Klonen im Vergleich zu übertragbaren Objekten

Auf einem MacBook Pro/10.6.8/2, 53 GHz/Intel Core 2 Duo war FF mit strukturiertem Klonen am schnellsten. Es dauerte durchschnittlich 302 ms, um die 32 MB große ArrayBuffer an einen Worker zu senden und wieder an den Hauptthread zurückzusenden (RRT – Round-Trip-Zeit). Im Vergleich dazu dauerte derselbe Test mit übertragbaren Assets 6, 6 ms. Das ist ein enormer Leistungsschub!

Dank dieser Geschwindigkeiten können riesige WebGL-Texturen/-Meshes nahtlos zwischen einem Worker und der Hauptanwendung übergeben werden.

Funktionserkennung

Die Funktion ist hier etwas schwierig zu erkennen. Ich empfehle, Ihrem Mitarbeiter eine kleine ArrayBuffer zu senden. Wenn der Puffer übertragen und nicht kopiert wird, wird sein .byteLength auf 0 gesetzt:

var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
    alert('Transferables are not supported in your browser!');
} else {
    // Transferables are supported.
}

Unterstützung:Derzeit Chrome 17 und höher, Firefox, Opera, Safari und IE 10 und höher

Aktualisiert (13.12.2011): Code-Snippet, das zeigt, dass sich die webkitPostMessage()-Signatur für Fenster und Worker unterscheidet. Aktualisiert (03.11.2016): Anbieterpräfixe entfernt und Code-Snippets aktualisiert