שיפורים ב-WebAssembly וב-WebGPU ל-AI באינטרנט מהיר יותר, חלק {8/}1

איך השיפורים ב-WebAssembly וב-WebGPU משפרים את הביצועים של למידת המכונה באינטרנט.

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

מסקנות AI באינטרנט

כולנו שמענו את הסיפור: AI משנה את העולם שלנו. האינטרנט אינו יוצא מן הכלל.

השנה נוספו ל-Chrome תכונות של AI גנרטיבי, כולל יצירת עיצוב מותאם אישית או עזרה בכתיבת טיוטה ראשונה של טקסט. אבל בינה מלאכותית היא הרבה יותר מכך: AI יכולה להעשיר את אפליקציות האינטרנט בעצמם.

בדפי אינטרנט אפשר להטמיע רכיבים חכמים לראייה, כמו זיהוי פנים או זיהוי תנועות, לסיווג אודיו או לזיהוי שפה. בשנה האחרונה ראינו עלייה בשימוש ב-AI גנרטיבי, כולל הדגמות מרשימות מאוד של מודלים גדולים של שפה (LLM) באינטרנט. כדאי לקרוא על Practical במכשיר AI למפתחי אתרים.

מסקנות AI באינטרנט זמינות היום בחלק גדול מהמכשירים, ועיבוד AI יכול להתבצע בדף האינטרנט עצמו, תוך שימוש בחומרה במכשיר של המשתמש.

התכונה הזו חזקה מכמה סיבות:

  • עלויות מופחתות: הסקת מסקנות לגבי לקוח הדפדפן מפחיתה משמעותית את עלויות השרתים, וזה יכול להיות שימושי במיוחד לשאילתות של בינה מלאכותית גנרטיבית, שיכולות להיות יקרות בהרבה משאילתות רגילות.
  • זמן אחזור: באפליקציות שרגישות במיוחד לזמן האחזור, כמו אפליקציות אודיו או וידאו, כל פעולות העיבוד שלכם במכשיר מקצרות את זמן האחזור.
  • פרטיות: לפעולה בצד הלקוח יש גם פוטנציאל לבטל סוג חדש של אפליקציות המחייבות פרטיות מוגברת, שבהן לא ניתן לשלוח נתונים לשרת.

איך עומסי עבודה (workloads) של AI רצים היום באינטרנט

כיום, מפתחי אפליקציות וחוקרים בונים מודלים באמצעות frameworks, מודלים פועלים בדפדפן באמצעות סביבת זמן ריצה כמו Tensorflow.js או ONNX Runtime Web. בסביבות זמני הריצה נעשה שימוש ב-Web APIs לביצוע.

כל זמני הריצה האלה מסתיימים בסופו של דבר במעבד (CPU) באמצעות JavaScript או WebAssembly או ב-GPU באמצעות WebGL או WebGPU.

תרשים של האופן שבו עומסי העבודה (workloads) של AI פועלים באינטרנט היום

עומסי עבודה (workloads) של למידת מכונה

עומסי עבודה של למידת מכונה (ML) דוחפים tensors דרך גרף של צמתים חישוביים. Tensor הם ערכי הקלט והפלט של הצמתים האלה שמבצעים כמות גדולה של פעולות חישוב על הנתונים.

נקודה זו חשובה מהסיבות הבאות:

  • טנזורים הם מבני נתונים גדולים מאוד, שמבצעים חישובים על מודלים שיכולים להיות בעלי מיליארדי משקלים
  • קנה מידה והסקת מסקנות יכולות להוביל למקבילות של נתונים. כלומר, אותן פעולות מבוצעות בכל הרכיבים שב-tensors.
  • בלמידת מכונה לא נדרש דיוק. ייתכן שתזדקקו למספר נקודה צפה (floating-point) של 64 ביט כדי לנחות על הירח, אבל יכול להיות שתזדקקו לים של מספרים באורך 8 ביט או פחות לצורך זיהוי פנים.

למרבה המזל, מעצבי הצ'יפים הוסיפו תכונות שמאפשרות להפעיל מודלים מהירים יותר ומגניבים יותר, ואפילו שאפשר יהיה להפעיל אותם בכלל.

בינתיים, כאן, בצוותי WebAssembly ו-WebGPU, אנחנו פועלים כדי לחשוף את היכולות החדשות האלה למפתחי אינטרנט. אם אתם מפתחי אפליקציות אינטרנט, סביר להניח שלא תשתמשו לעיתים קרובות בפרימיטיבים האלה. אנחנו צופים ששרשראות הכלים או המסגרות שבהן אתם משתמשים יתמכו בתכונות ובתוספים חדשים, ולכן תוכלו להפיק תועלת משינויים מזעריים בתשתית. אבל אם אתם רוצים לכוונן את האפליקציות שלכם לביצועים באופן ידני, התכונות האלה רלוונטיות לעבודה שלכם.

WebAssembly

WebAssembly (Wasm) הוא פורמט קוד קומפקטי ויעיל בבייט, שזמני ריצה יכולים להבין ולהפעיל. הוא תוכנן לנצל את יכולות החומרה הבסיסיות, כך שהוא יכול לפעול במהירויות נמוכות יותר. הקוד מאומת ומופעל בסביבה בטוחה בזיכרון, בארגז חול.

המידע של מודול Wasm מיוצג באמצעות קידוד בינארי צפוף. בהשוואה לפורמט מבוסס-טקסט, הקידוד הוא מהיר יותר, טעינה מהירה יותר ושימוש מופחת בזיכרון. הוא נייד בכך שהוא לא מניח הנחות לגבי הארכיטקטורה הבסיסית שאינן נפוצות כבר בארכיטקטורות מודרניות.

המפרט של WebAssembly הוא איטרטיבי, ועובדים עליו בקבוצת קהילה פתוחה של W3C.

בפורמט הבינארי אין הנחות לגבי סביבת המארח, ולכן הוא תוכנן לפעול היטב גם בהטמעות שאינן אתרים.

ניתן להדר את האפליקציה פעם אחת, ולהפעיל אותה בכל מקום: מחשב שולחני, מחשב נייד, טלפון או כל מכשיר אחר עם דפדפן. למידע נוסף על כך, ראו את המאמר כתיבה פעם אחת, הרצתם לכל מקום של סוף סוף באמצעות WebAssembly.

איור של מחשב נייד, טאבלט וטלפון

רוב האפליקציות בסביבת הייצור שפועלות בהן מסקנות מ-AI באינטרנט משתמשות ב-WebAssembly, גם למחשוב של המעבד (CPU) וגם לממשק עם מחשוב למטרה מיוחדת. באפליקציות מקוריות ניתן לגשת גם למחשוב לשימוש כללי וגם למחשוב למטרה מיוחדת, מכיוון שהאפליקציה יכולה לגשת ליכולות המכשיר.

באינטרנט, מטעמי ניידות ואבטחה, אנחנו מעריכים בקפידה אילו קבוצות של פרימיטיבים נחשפו. הדבר איזון בין הנגישות באינטרנט לבין הביצועים הטובים ביותר שמסופקים על ידי החומרה.

WebAssembly היא פישוט נייד של מעבדים (CPU), כך שכל הסקת המסקנות של Wasm פועלת במעבד (CPU). אומנם זו לא הבחירה שמניבה את הביצועים הטובים ביותר, אבל המעבדים (CPU) זמינים לכולם ועובדים עם רוב עומסי העבודה ברוב המכשירים.

בעומסי עבודה קטנים יותר, כמו עומסי עבודה של טקסט או אודיו, GPU יהיה יקר. יש כמה דוגמאות מהתקופה האחרונה שבהן Wasm היא הבחירה הנכונה:

תוכלו לגלות עוד יותר הדגמות בקוד פתוח, כמו: whisper-tiny, llama.cpp ו-Gemma2B פועלת בדפדפן.

גישה שכוללת את כל האפליקציות

יש לבחור פרימיטיבים על סמך מודל למידת מכונה מסוים, תשתית אפליקציות וחוויית השימוש הכוללת באפליקציות שמיועדות למשתמשים

לדוגמה, בזיהוי סימני דרך של MediaPipe, ניתן להשוות את ההסקה מהמעבד (CPU) ואת ההסקה מ-GPU (במכשירים עם Apple M1), אבל יש מודלים שבהם השונות עשויה להיות גבוהה משמעותית.

בכל הנוגע לעומסי עבודה של למידת מכונה, אנחנו שוקלים תצוגה מקיפה של האפליקציה תוך האזנה למחברי framework ולשותפי אפליקציות כדי לפתח ולשלוח את השיפורים המבוקשים ביותר. הקטגוריות האלה מתחלקות באופן כללי לשלוש קטגוריות:

  • חשיפת תוספי מעבד (CPU) קריטיים לביצועים
  • הפעלת האפשרות של מודלים גדולים יותר
  • אפשר יכולת פעולה הדדית חלקה עם ממשקי API אחרים באינטרנט

חישוב מהיר יותר

במצב כזה, מפרט WebAssembly כולל רק קבוצה מסוימת של הוראות שאנחנו חושפים לאינטרנט. עם זאת, החומרה ממשיכה להוסיף הוראות חדשות יותר שמגדילות את הפער בין הביצועים של מודעות מותאמות לביצועים של WebAssembly.

חשוב לזכור: מודלים של למידת מכונה לא תמיד דורשים רמות דיוק גבוהות. SIMD רגוע הוא הצעה שמפחיתה חלק מהדרישות המחמירות שאינן דטרמיניזם, ומובילה ליצירת קודן מהיר יותר לפעולות וקטור מסוימות שהן נקודות חמות לביצועים. בנוסף, גרסה רגועה של SIMD כוללת מוצר חדש עם נקודות והוראות FMA להאצת עומסי העבודה הקיימים מפי 1.5 עד 3. הפריט נשלח ב-Chrome 114.

פורמט נקודה צפה של חצי דיוק משתמש ב-16 ביט עבור IEEE FP16 במקום ב-32-ביט המשמש לערכי דיוק יחיד. בהשוואה לערכים של דיוק יחיד, יש מספר יתרונות לשימוש בערכים ברמת דיוק חצי-דיוק, דרישות זיכרון מופחתות, שמאפשרות אימון ופריסה של רשתות נוירונים גדולות יותר וצמצום רוחב הפס של הזיכרון. רמת דיוק נמוכה יותר מאיצה את העברת הנתונים ופעולות מתמטיות.

דגמים גדולים יותר

מצביעי עכבר לזיכרון ליניארי של Wasm מיוצגים כמספרים שלמים של 32 ביט. יש לכך שתי השלכות: גודל הערימה (heap) מוגבל ל-4GB (כשלמחשבים יש הרבה יותר זיכרון RAM פיזי גדול מזה), וקוד האפליקציה שמטורגט ל-Wasm צריך להיות תואם לגודל מצביע של 32 ביט.

במיוחד במודלים גדולים כמו שלנו, טעינת המודלים האלה ב-WebAssembly יכולה להיות מגבילה. בהצעה של Memory64, ההגבלות האלה יוסרו על ידי זיכרון לינארי כך שהנפח שלו יהיה גדול מ-4GB ותתאם לשטח הכתובות של הפלטפורמות המקוריות.

ב-Chrome אנחנו מיישמים הטמעה מלאה וצפויה להיות זמינה בהמשך השנה. בינתיים, אפשר להריץ ניסויים עם הדגל chrome://flags/#enable-experimental-webassembly-features, ולשלוח לנו משוב.

יכולת פעולה הדדית טובה יותר באינטרנט

WebAssembly יכולה להיות נקודת הכניסה לחישובים מיוחדים באינטרנט.

אפשר להשתמש ב-WebAssembly כדי להעביר אפליקציות GPU לאינטרנט. כלומר, אותה אפליקציית C++ שיכולה לפעול במכשיר יכולה לפעול גם באינטרנט, עם שינויים קטנים.

Emscripten, כלי המהדר של Wasm, כבר כולל קישורים ל-WebGPU. זו נקודת הכניסה להסקת הבינה המלאכותית באינטרנט, ולכן חשוב מאוד ש-Wasm תוכל לשתף פעולה בצורה חלקה עם שאר פלטפורמת האינטרנט. אנחנו עובדים על כמה הצעות שונות בתחום הזה.

שילוב הבטחה של JavaScript (JSPI)

יישומים אופייניים ל-C ו-C++ (כמו גם שפות רבות אחרות) נכתבים בדרך כלל מול ממשק API סינכרוני. המשמעות היא שהאפליקציה תפסיק את ההפעלה עד שהפעולה תושלם. אפליקציות חסימה כאלה הן בדרך כלל אינטואיטיביות יותר לכתיבה מאשר אפליקציות שמותאמות לא-סינכרוניות.

כשפעולות יקרות חוסמות את ה-thread הראשי, הן עלולות לחסום קלט/פלט (I/O) וה-jank גלוי למשתמשים. יש חוסר התאמה בין מודל תכנות סינכרוני של אפליקציות מקוריות לבין המודל האסינכרוני של האינטרנט. הדבר בעייתי במיוחד באפליקציות מדור קודם, שהעברה שלהן תהיה יקרה. Emscripten מספק דרך לעשות זאת באמצעות Asyncify, אבל זו לא תמיד האפשרות הטובה ביותר – קוד גדול יותר ולא יעיל באותה מידה.

הדוגמה הבאה היא חישוב פיבונאצ'י, תוך שימוש בהבטחות של 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

בדוגמה הזו, כדאי לשים לב לנקודות הבאות:

  • המאקרו EM_ASYNC_JS יוצר את כל קוד השיוך הדרוש כדי שנוכל להשתמש ב-JSPI כדי לגשת לתוצאה של ההבטחה, בדיוק כמו במקרה של פונקציה רגילה.
  • האפשרות המיוחדת של שורת הפקודה, -s ASYNCIFY=2. פעולה זו מפעילה את האפשרות ליצור קוד שמשתמש ב-JSPI כדי להתממשק עם ייבוא JavaScript שמחזיר הבטחות.

למידע נוסף על JSPI, על אופן השימוש בו ועל היתרונות שלו, אפשר לקרוא את המאמר חדש: WebAssembly JavaScript Promise Integration API בגרסה v8.dev. מידע נוסף על גרסת המקור לניסיון הנוכחי.

בקרת זיכרון

למפתחים יש שליטה מועטה מאוד על זיכרון Wasm; למודול יש זיכרון משלו. צריך להעתיק או להעתיק כל ממשקי API שצריכים גישה לזיכרון הזה, והשימוש הזה עשוי להצטבר. לדוגמה, ייתכן שאפליקציית גרפיקה תצטרך להעתיק ולהעתיק כל מסגרת בנפרד.

ההצעה של בקרת הזיכרון נועדה לספק שליטה ברמת פירוט גבוהה יותר בזיכרון הלינארי של Wasm ולצמצם את מספר העותקים בכל צינור עיבוד הנתונים של האפליקציות. הצעה זו נמצאת בשלב 1, אנו יוצרים את האב טיפוס ב-V8, מנוע ה-JavaScript של Chrome, כדי לסייע בהתפתחות של התקן.

להחליט איזה קצה עורפי מתאים לכם

המעבד (CPU) נמצא בכל מקום, אבל הוא לא תמיד האפשרות הטובה ביותר. מחשוב למטרה מיוחדת ב-GPU או במאיצים יכול להציע ביצועים גבוהים בהרבה, במיוחד בדגמים גדולים יותר ובמכשירים מתקדמים. הדבר נכון גם לאפליקציות מקוריות וגם ליישומי אינטרנט.

בחירת הקצה העורפי שתבחרו תלויה באפליקציה, ב-framework או ב-toolchain, וגם בגורמים אחרים שמשפיעים על הביצועים. עם זאת, אנחנו ממשיכים להשקיע בהצעות שמאפשרות לליבה של Wasm לפעול היטב עם שאר פלטפורמת האינטרנט, ובמיוחד עם WebGPU.

להמשך קריאה של חלק 2