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 przeciwieństwie do 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ć arrayBuffer.

Demonstracja porównawcza

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

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

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

Na MacBooku Pro/10.6.8/2.53 GHz/Intel Core 2 Duo najszybszy był FF z wykorzystaniem strukturalnego klonowania. Wysyłanie danych o rozmiary 32 MB ArrayBuffer do workera i publikowanie ich z powrotem w głównym wątku (RRT – czas błądzenia) zajęło średnio 302 ms. W przypadku transferów porównywalny test trwał 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 17+, Firefox, Opera, Safari i IE 10+.

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