
W tym artykule omówimy przypadek przyspieszenia aplikacji przeglądarkowej poprzez zastąpienie obliczeń JavaScript WebAssembly.
Czym jest WebAssembly?
W skrócie, to binarny format instrukcji dla maszyny wirtualnej stosu. Często nazywa się go językiem programowania, ale to nieprawda. Format instrukcji jest wykonywany w przeglądarce obok JavaScript.
Ważne jest, że WebAssembly można uzyskać poprzez kompilację źródeł w takich językach, jak C/C++, Rust, Go. Zastosowano tutaj statyczne typowanie i tak zwaną uproszczoną model pamięci. Kod, jak wspomniano, jest przechowywany w kompaktowym formacie binarnym, co umożliwia wykonywanie go prawie tak szybko, jakby aplikacja była uruchamiana za pomocą wiersza poleceń. Te możliwości przyczyniły się do wzrostu popularności WebAssembly.
Przypominamy: dla wszystkich czytelników „Habra” — zniżka 10 000 rubli przy zapisie na dowolny kurs Skillbox z kodem promocyjnym „Habra”.
Skillbox poleca: Praktyczny kurs .
Obecnie Wasm jest wykorzystywany w wielu aplikacjach, od gier takich jak Doom 3 po portowane aplikacje webowe, takie jak Autocad i Figma. Wasm jest także stosowany w obszarze obliczeń bezserwerowych.
W tym artykule przedstawimy przykład zastosowania Wasm do przyspieszenia analitycznej usługi webowej. Dla zobrazowania wzięliśmy działającą aplikację napisaną w C, która zostanie skompilowana do WebAssembly. Wynik zostanie wykorzystany do zastąpienia mało wydajnych fragmentów JS.
Transformacja aplikacji
W przykładzie użyjemy serwisu przeglądarkowego fastq.bio, który jest przeznaczony dla genetyków. Narzędzie pozwala ocenić jakość sekwencjonowania (czytania) DNA.
Oto przykład aplikacji w działaniu:

Szczegóły procesu nie są warte przytaczania, ponieważ są dość skomplikowane dla laików, ale jeśli krótko, to naukowcy mogą na podstawie powyższej infografiki zrozumieć, czy proces sekwencjonowania DNA przebiegł płynnie i jakie problemy wystąpiły.
Ten serwis ma swoje alternatywy, aplikacje desktopowe. Jednak fastq.bio pozwala na przyspieszenie pracy, wizualizując dane. W większości innych przypadków trzeba umieć pracować z wierszem poleceń, ale nie wszyscy genetycy mają potrzebne doświadczenie.
Wszystko działa prosto. Na wejściu znajdują się dane przedstawione w formie pliku tekstowego. Plik ten jest generowany przez specjalistyczne narzędzia do sekwencjonowania. Zawiera listę sekwencji DNA oraz ocenę jakości dla każdego nukleotydu. Format pliku to .fastq, stąd nazwa usługi.
Implementacja w JavaScript
Pierwszym krokiem użytkownika przy pracy z fastq.bio jest wybór odpowiedniego pliku. Używając obiektu File, aplikacja odczytuje losowy zestaw danych z pliku i przetwarza tę paczkę. Zadaniem JavaScript tutaj jest wykonywanie prostych operacji na ciągach znaków i obliczanie wskaźników. Jednym z nich jest liczba nukleotydów A, C, G i T w różnych fragmentach DNA.
Po obliczeniu potrzebnych wskaźników są one wizualizowane za pomocą Plotly.js, a usługa rozpoczyna pracę z nowym zestawem danych. Podział na fragmenty został wprowadzony w celu poprawy jakości UX. Pracując z wszystkimi danymi jednocześnie, proces może zawiesić się na pewien czas, ponieważ pliki z wynikami sekwencjonowania zajmują setki gigabajtów przestrzeni dyskowej. Usługa natomiast przetwarza fragmenty danych o wielkości od 0,5 do 1 MB, działając z nimi etapami i wytwarzając dane graficzne.
Oto jak to działa:

W czerwonym prostokącie umieszczony jest algorytm przekształceń ciągów do uzyskania wizualizacji. To najbardziej obciążona obliczeniowo część usługi. Warto spróbować zastąpić go Wasm.
Testujemy WebAssembly
Aby ocenić możliwość użycia Wasm, zespół projektu podjął się poszukiwania gotowych rozwiązań do stworzenia metryki QC (QC — kontrola jakości) na podstawie plików fastq. Poszukiwania prowadzone były wśród narzędzi napisanych w C, C++ lub Rust, aby była możliwość portowania kodu na WebAssembly. Dodatkowo narzędzie nie powinno być 'surowe', wymagany był serwis już przetestowany przez naukowców.
W rezultacie dokonano wyboru na rzecz . Aplikacja jest dość popularna, jest open-source, a język źródłowy to C.
Przed przekształceniem do Wasm warto zapoznać się z zasadą kompilacji seqtk dla komputerów stacjonarnych. Zgodnie z Makefile, oto co jest potrzebne:
# Compile to binary
$ gcc seqtk.c
-o seqtk
-O2
-lm
-lzW zasadzie, można skompilować seqtk za pomocą Emscripten. Jeśli go brakuje, można obejść się .
$ docker pull robertaboukhalil/emsdk:1.38.26
$ docker run -dt --name wasm-seqtk robertaboukhalil/emsdk:1.38.26W razie potrzeby , ale wymaga to czasu.
Wewnątrz kontenera można łatwo użyć emcc jako alternatywy dla gcc:
# Compile to WebAssembly
$ emcc seqtk.c
-o seqtk.js
-O2
-lm
-s USE_ZLIB=1
-s FORCE_FILESYSTEM=1Minimalne zmiany:
Zamiast wyjścia do pliku binarnego Emscripten generuje pliki .wasm i .js, które są używane do uruchamiania modułu WebAssembly.
Aby wspierać bibliotekę zlib, używa się flagi USE_ZLIB. Biblioteka jest powszechna i przeniesiona na WebAssembly, a Emscripten włącza ją do projektu.
Aktywuje się wirtualny system plików Emscripten. To , działający w pamięci operacyjnej wewnątrz przeglądarki. Gdy strona jest odświeżana, pamięć jest czyszczona.
Aby zrozumieć, dlaczego potrzebny jest wirtualny system plików, warto porównać sposób uruchamiania seqtk z linii poleceń ze sposobem uruchamiania skompilowanego modułu WebAssembly.
# On the command line
$ ./seqtk fqchk data.fastq
# In the browser console
> Module.callMain(["fqchk", "data.fastq"])Dostęp do wirtualnego systemu plików jest konieczny, aby nie przerabiać seqtk na wejście ze stringów, a nie plików. W takim przypadku fragment danych jest wyświetlany jako plik data.fastq w wirtualnym systemie plików przy wywołaniu main() seqtk.
Oto nowa architektura:

Ilustracja pokazuje, że zamiast obliczeń w głównym wątku przeglądarki używane są . Taki sposób pozwala na wykonywanie obliczeń w tle, nie pogarszając responsywności przeglądarki. Kontroler WebWorkera uruchamia worker, zarządzając jego interakcją z głównym wątkiem.
Komenda seqtk jest uruchamiana za pomocą workera na zamontowanym pliku. Po zakończeniu pracy worker zwraca wynik w postaci Promise. Gdy wiadomość zostanie odebrana przez główny wątek, wynik jest używany do aktualizacji wykresów. I tak przez kilka iteracji.
Co z wydajnością WebAssembly?
Aby ocenić zmianę wydajności, zespół projektu wykorzystał parametr liczby operacji odczytu na sekundę. Czas budowania interaktywnych wykresów nie jest uwzględniany, ponieważ w obu implementacjach używany jest JavaScript.
Korzyści z użycia rozwiązania 'z pudełka' wyniosły dziewięć razy.

To świetny wynik, ale okazuje się, że można go również zoptymalizować. Chodzi o to, że wiele wyników analizy QC nie jest wykorzystywanych przez seqtk, więc można je usunąć. Jeśli to się zrobi, wynik w porównaniu do JS poprawia się 13 razy.

Osiągnięto to poprzez proste skomentowanie poleceń printf().
Ale to nie wszystko. Chodzi o to, że na tym etapie fastq.bio otrzymuje wyniki analizy z wywołania różnych funkcji C. Każda z nich oblicza swój zestaw charakterystyk, więc każdy fragment pliku był odczytywany dwa razy.
Aby rozwiązać ten problem, postanowiono połączyć dwie funkcje w jedną. W rezultacie wydajność wzrosła 20 razy.

Warto zauważyć, że tak wyjątkowy wynik można osiągnąć nie zawsze. W niektórych przypadkach wydajność spada, dlatego warto ocenić każdy konkretny przypadek.
Podsumowując, można powiedzieć, że Wasm rzeczywiście daje możliwość poprawy wydajności aplikacji, ale należy go używać z rozwagą.
Skillbox poleca:
- Dwuletni praktyczny kurs .
- Kurs online .
- Praktyczny roczny kurs .
Źródło: habr.com
