Ulepszenia WebAssembly i WebGPU umożliwiające szybsze działanie sztucznej inteligencji, część 1

Dowiedz się, jak ulepszenia w WebAssembly i WebGPU zwiększają wydajność systemów uczących się w internecie.

Austin Eng
Austin Eng
Deepti Gandluri
Deepti Gandluri
François Beaufort
François Beaufort

wnioskowanie przez AI w internecie.

Wszyscy słyszeliśmy tę historię: AI zmienia nasz świat. Internet nie jest wyjątkiem.

W tym roku dodaliśmy do Chrome funkcje generatywnej AI, w tym możliwość tworzenia niestandardowych motywów i pomocy w napisaniu pierwszej wersji roboczej tekstu. AI to jednak znacznie więcej. AI może same wzbogacać aplikacje internetowe.

Strony internetowe mogą zawierać inteligentne komponenty rozpoznawania twarzy, np. rozpoznawania twarzy lub gestów, klasyfikacji dźwięku i wykrywania języka. W zeszłym roku byliśmy świadkami rozwoju generatywnej AI, w tym imponujących demonstracji dużych modeli językowych (LLM) w internecie. Zapoznaj się z artykułem na temat praktycznej AI na urządzeniu dla programistów stron internetowych.

Obecnie wnioskowanie przez AI w internecie jest dostępne w przypadku dużej sekcji urządzeń, a przetwarzanie AI może odbywać się bezpośrednio na stronie internetowej z wykorzystaniem sprzętu w urządzeniu użytkownika.

Jest to przydatne z kilku powodów:

  • Mniejsze koszty: wnioskowanie na podstawie klienta przeglądarki znacznie obniża koszty serwera, co może być szczególnie przydatne w przypadku zapytań generatywnej AI, które mogą być znacznie droższe niż zwykłe zapytania.
  • Czas oczekiwania: w przypadku aplikacji, które są szczególnie wrażliwe na czas oczekiwania (np. audio lub wideo), zapewnienie, że wszystkie przetwarzanie odbywa się na urządzeniu, przekłada się na krótszy czas oczekiwania.
  • Prywatność: działanie po stronie klienta może również odblokować nową klasę aplikacji wymagających większej prywatności, w przypadku których dane nie mogą być wysyłane na serwer.

Jak obecnie zadania AI uruchamiają się w internecie

Obecnie programiści aplikacji i badacze tworzą modele przy użyciu platform, modele są wykonywane w przeglądarce za pomocą środowiska wykonawczego, takiego jak Tensorflow.js czy ONNX Runtime Web, a środowiska wykonawcze wykorzystują interfejsy API do wykonywania.

W końcu wszystkie takie środowiska wykonawcze działają na CPU przez JavaScript lub WebAssembly albo w GPU przez WebGL lub WebGPU.

Schemat przedstawiający aktualny sposób działania zadań AI w sieci

Zadania systemów uczących się

Zadania systemów uczących się przekazują tensory za pomocą grafu węzłów obliczeniowych. Tensory to dane wejściowe i wyjściowe tych węzłów, które wykonują duże ilości obliczeń na danych.

To ważne, ponieważ:

  • Tensory to bardzo duże struktury danych wykonujące obliczenia na modelach, które mogą mieć miliardy wag
  • Skalowanie i wnioskowanie może prowadzić do równoległości danych. Oznacza to, że wykonywane są te same operacje na wszystkich elementach tensorów.
  • Systemy uczące się nie wymagają precyzji. Aby wylądować na Księżycu, możesz potrzebować 64-bitowej liczby zmiennoprzecinkowej, ale do rozpoznawania twarzy wystarczy Ci morze o długości maksymalnie 8-bitowej.

Na szczęście projektanci układów scalonych mają dodatkowe funkcje, dzięki którym modele działają szybciej i chłodniej, a nawet umożliwiają ich uruchomienie.

Tymczasem w zespołach WebAssembly i WebGPU pracujemy nad udostępnieniem tych nowych możliwości programistom stron internetowych. Jeśli programujesz aplikacje internetowe, najprawdopodobniej nie będziesz często używać tych podstawowych elementów. Spodziewamy się, że używane przez Ciebie łańcuchy narzędzi lub platformy będą obsługiwać nowe funkcje i rozszerzenia, co oznacza, że niewielkie zmiany w Twojej infrastrukturze przyniosą Ci korzyści. Jeśli jednak chcesz ręcznie dostroić swoje aplikacje pod kątem wydajności, funkcje te będą przydatne w Twojej pracy.

WebAssembly

WebAssembly (Wasm) to kompaktowy i wydajny format kodu bajtowego zrozumiały i przetwarzany przez środowiska wykonawcze. Został zaprojektowany z myślą o wykorzystaniu podstawowych możliwości sprzętowych, dzięki czemu może działać z szybkością zbliżonym do natywnej. Kod jest weryfikowany i uruchamiany w bezpiecznym w pamięci środowisku piaskownicy.

Informacje o module Wasm są przedstawiane za pomocą gęstego kodowania binarnego. W porównaniu z formatem opartym na tekście oznacza to szybsze dekodowanie, szybsze ładowanie i mniejsze wykorzystanie pamięci. Jest poręczna w tym sensie, że nie uwzględnia założeń dotyczących podstawowej architektury, które nie są jeszcze powszechne w nowoczesnej architekturze.

Specyfikacja WebAssembly ma charakter iteracyjny i jest rozwijana w otwartej grupie społeczności W3C.

Format binarny nie przyjmuje żadnych założeń dotyczących środowiska hosta, dlatego jest zaprojektowany tak, aby dobrze sprawdzał się również w przypadku wektorów dystrybucyjnych innych niż internetowe.

Aplikację możesz skompilować raz i uruchomić ją wszędzie: na komputerze, laptopie, telefonie lub dowolnym innym urządzeniu z przeglądarką. Więcej informacji na ten temat znajdziesz w artykule Pisanie raz i uruchamianie w dowolnym miejscu, w którym możliwe jest wdrożenie przy użyciu WebAssembly.

Ilustracja: laptopa, tablet i telefon

Większość aplikacji produkcyjnych, które wykorzystują wnioskowanie z AI w internecie, korzysta z WebAssembly zarówno do przetwarzania CPU, jak i interakcji z obliczeniami specjalnymi. W przypadku aplikacji natywnych można korzystać zarówno z przetwarzania ogólnego, jak i specjalnego, ponieważ aplikacja ma dostęp do możliwości urządzenia.

W internecie, ze względu na przenośność i bezpieczeństwo, dokładnie oceniamy, który zestaw podstawowych elementów jest ujawniony. Zapewnia to równowagę między ułatwieniami dostępu w internecie a maksymalną wydajnością zapewnianą przez sprzęt.

WebAssembly to przenośna abstrakcja procesorów, więc całe wnioskowanie Wasm jest wykonywane na procesorze. Chociaż nie jest to najbardziej wydajny wybór, procesory są powszechnie dostępne i działają w większości zadań na większości urządzeń.

W przypadku mniejszych zbiorów zadań, takich jak zadania tekstowe lub audio, użycie GPU jest drogie. Oto kilka najnowszych przykładów, w których przypadku Wasm to właściwy wybór:

Jeszcze więcej informacji znajdziesz w wersjach demonstracyjnych typu open source, takich jak: whisper-tiny, llama.cpp czy Gemma2B działające w przeglądarce.

Podejście całościowe do aplikacji

Podstawowe elementy należy wybrać na podstawie konkretnego modelu ML, infrastruktury aplikacji i ogólnego docelowego środowiska aplikacji dla użytkowników

Na przykład w przypadku wykrywania punktów orientacyjnych twarzy w MediaPipe wnioskowanie dotyczące procesora oraz GPU jest porównywalne (działa na urządzeniu Apple M1), ale istnieją modele, w których te różnice mogą być znacznie większe.

Jeśli chodzi o zadania ML, bierzemy pod uwagę ogólny widok aplikacji, słuchając opinii autorów platformy i partnerów aplikacji, aby opracować i wdrożyć najbardziej pożądane ulepszenia. Ogólnie można je podzielić na 3 kategorie:

  • Udostępnianie rozszerzeń procesora o kluczowym znaczeniu dla wydajności
  • Włącz uruchamianie większych modeli
  • Włącz płynną współpracę z innymi internetowymi interfejsami API

Szybsze obliczenia

Obecnie specyfikacja WebAssembly zawiera tylko określony zestaw instrukcji, które udostępniamy w internecie. Sprzęt wciąż dodaje jednak nowsze instrukcje, które zwiększają lukę między wydajnością natywnym a WebAssembly.

Pamiętaj, że modele ML nie zawsze wymagają wysokiego poziomu dokładności. Relaxed SIMD to propozycja, która zmniejsza niektóre rygorystyczne wymagania dotyczące niedeterminizmu, co prowadzi do szybszego generowania kodu w przypadku niektórych operacji wektorowych, które mają największe znaczenie dla wydajności. Ponadto łagodzenie skutków SIMD wprowadza nową usługę kropkową i instrukcje FMA, które przyspieszają istniejące zadania od 1,5 do 3 razy. Ta funkcja jest dostępna w Chrome 114.

Format zmiennoprzecinkowy o połowie dokładności używa 16-bitowych bitów dla IEEE FP16, a nie 32-bitowych używanych do pojedynczych wartości dokładności. W porównaniu z pojedynczymi wartościami dokładności stosowanie wartości o połowiowej dokładności ma kilka zalet, m.in. mniejsze wymagania dotyczące pamięci, które umożliwiają trenowanie i wdrażanie większych sieci neuronowych oraz mniejszą przepustowość pamięci. Zmniejszona precyzja przyspiesza przesyłanie danych i wykonywanie operacji matematycznych.

Większe modele

Wskaźniki do liniowej pamięci Wasm są przedstawiane jako 32-bitowe liczby całkowite. Ma to dwie konsekwencje: rozmiar stosu jest ograniczony do 4 GB (gdy komputery mają znacznie więcej fizycznej pamięci RAM), a kod aplikacji kierowany na Wasm musi być zgodny z 32-bitowym wskaźnikiem (czyli).

Szczególnie w przypadku dużych modeli, takich jak te obecne, wczytywanie ich do WebAssembly może być ograniczające. Propozycja Memory64 usuwa te ograniczenia przez pamięć linearną, tak aby były większe niż 4 GB i pasują do przestrzeni adresowej platform natywnych.

Mamy już w pełni działającą implementację w Chrome. Planujemy wprowadzić ją jeszcze w tym roku. Na razie możesz przeprowadzać eksperymenty z flagą chrome://flags/#enable-experimental-webassembly-features i wysyłać nam opinie.

Lepsza interoperacyjność w internecie

WebAssembly może stanowić punkt wyjścia dla specjalnych procesów obliczeniowych w internecie.

Za pomocą WebAssembly możesz umieszczać aplikacje GPU w internecie. Oznacza to, że ta sama aplikacja w języku C++, którą można uruchomić na urządzeniu, może też działać w internecie, choć wymaga tylko niewielkich modyfikacji.

Emscripten – łańcuch narzędzi kompilatora Wasm – ma już powiązania dla WebGPU. Jest to punkt początkowy wnioskowania z wykorzystaniem AI w internecie, dlatego ważne jest, aby Wasm mógł płynnie współdziałać z resztą platformy internetowej. Pracujemy nad różnymi propozycjami w tym obszarze.

Integracja kodu JavaScript z obietnicą (JSPI)

Typowe aplikacje w językach C i C++ (a także w wielu innych językach) są zwykle napisane za pomocą synchronicznego interfejsu API. Oznacza to, że do czasu zakończenia operacji aplikacja zostanie zatrzymana. Tworzenie takich aplikacji blokujących jest zazwyczaj bardziej intuicyjne niż w przypadku aplikacji asynchronicznych.

Gdy kosztowne operacje blokują wątek główny, mogą blokować wejścia/wyjścia, przez co użytkownicy widzą zadymienie. Występuje niezgodność między synchronicznym modelem programowania aplikacji natywnych a modelem asynchronicznym sieci. Jest to szczególnie problematyczne w przypadku starszych aplikacji, których przeniesienie byłoby kosztowne. Umożliwia to funkcja Asyncify, ale nie zawsze jest to najlepsze rozwiązanie – większy rozmiar kodu i mniej wydajne.

Następujący przykład przedstawia obliczanie fibonacci z wykorzystaniem obietnic dodanych w języku JavaScript.

long promiseFib(long x) {
 if (x == 0)
   return 0;
 if (x == 1)
   return 1;
 return promiseAdd(promiseFib(x - 1), promiseFib(x - 2));
}
// promise an addition
EM_ASYNC_JS(long, promiseAdd, (long x, long y), {
  return Promise.resolve(x+y);
});
emcc -O3 fib.c -o b.html -s ASYNCIFY=2

W tym przykładzie zwróć uwagę na te kwestie:

  • Makro EM_ASYNC_JS generuje cały niezbędny kod typu glue, dzięki czemu możemy używać JSPI do uzyskiwania dostępu do wyniku obietnicy, tak jak w przypadku normalnej funkcji.
  • Specjalna opcja wiersza poleceń -s ASYNCIFY=2. Wywołuje to opcję generowania kodu, który używa JSPI do łączenia się z importami JavaScript zwracającymi obietnice.

Więcej informacji o języku JSPI, korzystaniu z niego i jego zaletach znajdziesz w artykule Przedstawiamy interfejs WebAssembly JavaScript Promise Integration API w wersji v8.dev. Dowiedz się więcej o bieżącej wersji próbnej origin.

Kontrola pamięci

Deweloperzy mają bardzo małą kontrolę nad pamięcią Wasm. moduł ma własną pamięć. Każdy interfejs API, który potrzebuje dostępu do tej pamięci, musi zostać skopiowany lub skopiowany, a wykorzystanie może się sumować. Na przykład aplikacja graficzna może wymagać kopiowania i kopiowania każdej ramki.

Propozycja kontroli pamięci ma na celu zapewnienie bardziej szczegółowej kontroli nad pamięcią linearną Wasm i zmniejszenie liczby kopii w potoku aplikacji. Ta propozycja jest w fazie pierwszej. Tworzymy ją w prototypie w V8, mechanizmie JavaScript Chrome, w celu dalszego rozwoju standardu.

Wybieranie odpowiedniego backendu

Choć procesory są dostępne, nie zawsze jest to najlepsze rozwiązanie. Specjalne obliczenia w GPU lub akceleratorach mogą zapewnić znacznie większą wydajność, zwłaszcza w przypadku większych modeli i zaawansowanych urządzeń. Dotyczy to zarówno aplikacji natywnych, jak i internetowych.

Wybór backendu zależy od aplikacji, platformy lub łańcucha narzędzi, a także od innych czynników wpływających na wydajność. Nadal inwestujemy w oferty, dzięki którym podstawowa usługa Wasm dobrze współpracuje z resztą platformy internetowej, a w szczególności z WebGPU.

Czytaj dalej, część 2