Obiekty, które można przenieść – błyskawiczna

W Chrome 13 wprowadzono wysyłanie ArrayBuffer do/z Web Worker za pomocą algorytmu o nazwie strukturalne klonowanie. Dzięki temu interfejs API postMessage() może przyjmować wiadomości, które nie są tylko ciągami znaków, ale też złożone typy, takie jak File, Blob, ArrayBuffer i obiekty JSON. Strukturalne klonowanie jest obsługiwane także w nowszych wersjach Firefoksa.

Szybciej znaczy lepiej

Strukturalne klonowanie jest świetne, ale nadal jest to operacja kopiowania. Przekazywanie danych o rozmiarze 32 MB ArrayBuffer do instancji roboczej może zająć setki milisekund. Nowe wersje przeglądarek zawierają ogromną poprawę wydajności przekazywania wiadomości, zwaną obiektmi przenośnymi.

W przypadku obiektów możliwych do przeniesienia dane są przenoszone z jednego kontekstu do drugiego. Jest to metoda bez kopiowania, która znacznie poprawia wydajność wysyłania danych do Workera. Jeśli pochodzisz ze świata C/C++, możesz traktować to jak przekazywanie przez referencję. Jednak w odróżnieniu od przekazywania przez odniesienie „wersja” z kontekstu wywołania nie jest już dostępna po przeniesieniu do nowego kontekstu. Na przykład podczas przenoszenia ArrayBuffer z aplikacji głównej do Workera oryginalny obiekt ArrayBuffer jest czyszczony i nie można go już używać. Jego zawartość jest (dosłownie) przenoszona do kontekstu Worker.

Aby grać z przenośnymi, dostępna jest nowa wersja postMessage(), która obsługuje przenośne obiekty:

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

W przypadku pracownika pierwszym argumentem jest wiadomość ArrayBuffer. Drugi argument to lista elementów, które mają zostać przeniesione. W tym przykładzie na liście do przeniesienia musisz podać wartość arrayBuffer.

Demonstracja porównawcza

Aby zobaczyć wzrost skuteczności, jaki dają elementy przenośne, przygotowałem prezentację demonstracyjną.

Demo wysyła ArrayBuffer o rozmiarze 32 MB do instancji roboczej i z powrotem, korzystając z funkcji postMessage(). Jeśli Twoja przeglądarka nie obsługuje przenośnych, próbka użyje klonowania ustrukturyzowanego. Średnia z 5 wykonań w różnych przeglądarkach:

Wykres porównujący klonowanie uporządkowane z obiektami podlegającymi przeniesieniu

Na komputerze MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo najszybszy był FF z wykorzystaniem strukturalnego klonowania. Wysłanie 32 MB danych (ArrayBuffer) do workera i przesłanie ich z powrotem do wątku głównego (RRT – czas błądzenia) zajęło średnio 302 ms. W przypadku transferów porównywalnych czas wykonania tego samego testu wynosił 6, 6 ms. To ogromny wzrost wydajności.

Dzięki takim prędkościom można bezproblemowo przekazywać duże tekstury lub siatki WebGL między Workerem a aplikacją główną.

Wykrywanie funkcji

Wykrywanie tej funkcji jest nieco trudne. Zalecamy wysłanie pracownikowi niewielkiej ArrayBuffer. Jeśli bufor zostanie przeniesiony, a nie skopiowany, jego wartość .byteLength będzie wynosić 0:

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

Obsługa: obecnie Chrome w wersji 17 lub nowszej, Firefox, Opera, Safari i IE w wersji 10 lub nowszej.

Aktualizacja (13.12.2011): fragment kodu, który wyświetla podpis webkitPostMessage(), jest inny w przypadku okna i elementu worker. Aktualizacja (3 listopada 2016 r.): usunięto prefiksy dostawców i zaktualizowano fragmenty kodu