איך שיפורים ב-WebAssembly וב-WebGPU משפרים את הביצועים של למידת המכונה באינטרנט
מסקנות של AI באינטרנט
כולנו שמענו את הסיפור: AI משנה את העולם שלנו. האינטרנט לא יוצא מן הכלל.
השנה הוספנו ל-Chrome תכונות של AI גנרטיבי, כולל יצירת עיצובים מותאמים אישית או עזרה בכתיבת טיוטה ראשונה של טקסט. אבל AI הוא הרבה יותר מזה. הוא יכול להעשיר את אפליקציות האינטרנט עצמן.
דפי אינטרנט יכולים לכלול רכיבים חכמים לראייה, כמו זיהוי פנים או זיהוי תנועות, לסיווג אודיו או לזיהוי שפה. בשנה האחרונה ראינו עלייה חדה בשימוש ב-AI גנרטיבי, כולל כמה הדגמות מרשימים מאוד של מודלים גדולים של שפה באינטרנט. מומלץ לעיין במאמר שימוש מעשי ב-AI במכשיר למפתחי אינטרנט.
כיום, ניתן להשתמש בהסקת מסקנות מבוססת-AI באינטרנט במגוון רחב של מכשירים, ועיבוד AI יכול להתבצע בדף האינטרנט עצמו, תוך ניצול החומרה במכשיר של המשתמש.
יש לכך כמה יתרונות:
- עלויות מופחתות: הפעלת ההסקה בלקוח הדפדפן מפחיתה באופן משמעותי את עלויות השרת, ויכולה להיות שימושית במיוחד לשאילתות של AI גנרטיבי, שעלויות הביצוע שלהן עשויות להיות גבוהות פי כמה משאילתות רגילות.
- זמן אחזור: באפליקציות שרגישות במיוחד לזמן אחזור, כמו אפליקציות אודיו או וידאו, ביצוע כל העיבוד במכשיר מפחית את זמן האחזור.
- פרטיות: הפעלה בצד הלקוח יכולה גם לאפשר לפתח סוג חדש של אפליקציות שדורשות רמת פרטיות גבוהה יותר, שבהן אי אפשר לשלוח נתונים לשרת.
איך עומסי העבודה של AI פועלים באינטרנט היום
כיום, מפתחי אפליקציות וחוקרים יוצרים מודלים באמצעות מסגרות, המודלים פועלים בדפדפן באמצעות סביבת זמן ריצה כמו Tensorflow.js או ONNX Runtime Web, וסביבות זמן הריצה משתמשות ב-Web APIs לצורך ביצוע.
כל סביבות זמן הריצה האלה מובילות בסופו של דבר לריצה במעבד (CPU) דרך JavaScript או WebAssembly, או ב-GPU דרך WebGL או WebGPU.
עומסי עבודה של למידת מכונה
עומסי עבודה של למידת מכונה (ML) דוחפים טינסורים דרך תרשים של צמתים מחשוביים. Tensors הם הקלט והפלט של הצמתים האלה, שמבצעים כמות גדולה של חישובים על הנתונים.
זה חשוב כי:
- טינסורים הם מבני נתונים גדולים מאוד, שמבצעים חישובים במודלים שיכולים לכלול מיליארדי משקלים.
- התאמה לעומס והסקה יכולות להוביל למקבילות נתונים. המשמעות היא שאותן פעולות מתבצעות בכל הרכיבים בטנסורים.
- ב-ML לא נדרש דיוק. יכול להיות שתצטרכו מספר של 64 ביט עם נקודה צפה כדי לנחות על הירח, אבל יכול להיות שתצטרכו רק ים של מספרים של 8 ביט או פחות כדי לבצע זיהוי פנים.
למרבה המזל, מעצבי הצ'יפים הוסיפו תכונות שבעזרתן המודלים פועלים מהר יותר, פחות חמים ואפילו אפשר להפעיל אותם בכלל.
בינתיים, בצוותים של WebAssembly ו-WebGPU אנחנו פועלים כדי לחשוף את היכולות החדשות האלה למפתחי אינטרנט. אם אתם מפתחים של אפליקציות אינטרנט, סביר להניח שלא תשתמשו בתכונות הפרימיטיביות ברמה הנמוכה הזו לעיתים קרובות. אנחנו צופים שסביבות הפיתוח או המסגרות שבהן אתם משתמשים יתמכו בתכונות ובתוספים חדשים, כך שתוכלו ליהנות מהיתרונות שלהם עם שינויים מינימליים בתשתית. אבל אם אתם אוהבים לשפר את הביצועים של האפליקציות באופן ידני, התכונות האלה רלוונטיות לכם.
WebAssembly
WebAssembly (Wasm) הוא פורמט קומפקטי ויעיל של קוד בייט שסביבות זמן ריצה יכולות להבין ולהריץ. הוא תוכנן לנצל את יכולות החומרה הבסיסיות, כך שהוא יכול לפעול במהירויות שדומות למהירויות של קוד מקורי. הקוד מאומת ומופעל בסביבה בטוחה לזיכרון, בסביבת חול.
המידע על מודול Wasm מיוצג באמצעות קידוד בינארי צפוף. בהשוואה לפורמט שמבוסס על טקסט, המשמעות היא פענוח מהיר יותר, טעינה מהירה יותר ושימוש מצומצם יותר בזיכרון. הוא נייד במובן הזה שהוא לא מבוסס על הנחות לגבי הארכיטקטורה הבסיסית שלא קיימות בארכיטקטורות מודרניות.
המפרט של WebAssembly הוא איטרטיבי, והוא מפותח בקבוצת קהילה פתוחה של W3C.
בפורמט הבינארי אין הנחות לגבי סביבת המארח, ולכן הוא מיועד לפעול גם בהטמעות שלא באינטרנט.
אפשר לקמפל את האפליקציה פעם אחת ולהריץ אותה בכל מקום: במחשב נייח, במחשב נייד, בטלפון או בכל מכשיר אחר עם דפדפן. מידע נוסף זמין במאמר Write once, run anywhere finally realized with WebAssembly.
רוב האפליקציות בסביבת הייצור שמריצות שקלול של AI באינטרנט משתמשות ב-WebAssembly, גם לצורך חישוב ב-CPU וגם ליצירת ממשק עם חישוב למטרות מיוחדות. באפליקציות מקוריות, אפשר לגשת גם למחשוב למטרות כלליות וגם למחשוב למטרות מיוחדות, כי האפליקציה יכולה לגשת ליכולות של המכשיר.
באינטרנט, אנחנו בודקים היטב איזו קבוצה של רכיבים בסיסיים נחשפת, כדי לשמור על ניידות ואבטחה. כך אפשר לאזן בין הנגישות של האתר לבין הביצועים המקסימליים שהחומרה מספקת.
WebAssembly הוא הפשטה ניידת של מעבדים (CPU), כך שכל ההסקות של Wasm פועלות ב-CPU. זו לא האפשרות עם הביצועים הכי טובים, אבל מעבדים זמינים באופן נרחב ופועלים ברוב עומסי העבודה, ברוב המכשירים.
לעומסי עבודה קטנים יותר, כמו עומסי עבודה של טקסט או אודיו, שימוש ב-GPU יהיה יקר. יש כמה דוגמאות מהזמן האחרון שבהן Wasm הוא הבחירה הנכונה:
- Adobe משתמשת ב-Tensorflow.js כדי לשפר את Photoshop לאינטרנט.
- הוספנו ל-Google Meet טשטוש רקע, אחד מהאפקטים הראשונים של וידאו מבוססי-Wasm באינטרנט.
- ב-YouTube יש כמה אפקטים של מציאות רבודה.
- אפשר לערוך את התמונות ב-Google Photos אונליין.
אפשר למצוא עוד דוגמאות במצגות של קוד פתוח, כמו whisper-tiny, llama.cpp ו-Gemma2B שפועל בדפדפן.
גישה הוליסטית לאפליקציות
צריך לבחור את הפרימיטיבים על סמך מודל ה-ML הספציפי, תשתית האפליקציה וחוויית המשתמש הכוללת הרצויה באפליקציה.
לדוגמה, בזיהוי נקודות ציון בפנים של MediaPipe, ההסקה של המעבד וההסקה של המעבד הגרפי דומות (כשהן פועלות במכשיר Apple M1), אבל יש מודלים שבהם השונות עשויה להיות גבוהה יותר באופן משמעותי.
כשמדובר בעומסי עבודה של למידת מכונה, אנחנו מביאים בחשבון תצוגה מקיפה של האפליקציה, תוך כדי הקשבה לכותבי המסגרות ולשותפי האפליקציות, כדי לפתח ולשלוח את השיפורים הנדרשים ביותר. באופן כללי, הבעיות האלה נחלקות לשלוש קטגוריות:
- חשיפת תוספים של מעבדים קריטיים לביצועים
- הפעלת מודלים גדולים יותר
- הפעלת יכולת פעולה הדדית חלקה עם ממשקי Web API אחרים
חישוב מהיר יותר
נכון לעכשיו, מפרט WebAssembly כולל רק קבוצה מסוימת של הוראות שאנחנו חושפים לאינטרנט. עם זאת, לחומרה ממשיכים להוסיף הוראות חדשות שמגדילות את הפער בין הביצועים של קוד מקורי לבין הביצועים של WebAssembly.
חשוב לזכור: לא תמיד נדרשות רמות דיוק גבוהות במודלים של למידת מכונה. Relaxed SIMD היא הצעה שמפחיתה חלק מהדרישות המחמירות של אי-דטרמיניזם, וכתוצאה מכך מאפשרת יצירת קוד מהירה יותר לחלק מהפעולות על וקטורים שהן נקודות חמות בביצועים. בנוסף, ב-Relaxed SIMD יש הוראות חדשות של מכפלת מטריצות והוראות FMA שמאיצות את עומסי העבודה הקיימים פי 1.5 עד פי 3. השינוי הזה שוחרר ב-Chrome 114.
בפורמט של נקודת צפה ברמת דיוק חצי נעשה שימוש ב-16 ביט ל-IEEE FP16 במקום ב-32 הביט שמשמשים לערכים ברמת דיוק יחידה. בהשוואה לערכים ברמת דיוק יחידה, יש כמה יתרונות לשימוש בערכים ברמת דיוק חצי: דרישות זיכרון מופחתות שמאפשרות אימון ופריסה של רשתות עצביות גדולות יותר, רוחב פס זיכרון מופחת. דיוק מופחת מזרז את העברת הנתונים ואת הפעולות המתמטיות.
דגמים גדולים יותר
מצביעים לזיכרון לינארי של Wasm מיוצגים כמספרים שלמים של 32 ביט. יש לכך שתי השלכות: גודל האשפה מוגבל ל-4GB (כשבמחשבים יש הרבה יותר זיכרון RAM פיזי מזה), וקוד האפליקציה שמטרגט את Wasm חייב להיות תואם לגודל של מצביע 32 ביט (ש).
במיוחד עם מודלים גדולים כמו שיש לנו היום, טעינת המודלים האלה ב-WebAssembly יכולה להיות מגבילה. ההצעה Memory64 מסירה את ההגבלות האלה על ידי זיכרון לינארי גדול מ-4GB שתואמת למרחב הכתובות של פלטפורמות מקומיות.
יש לנו הטמעה מלאה ועובדת ב-Chrome, והיא צפויה לצאת בהמשך השנה. בינתיים, אתם יכולים להריץ ניסויים עם הדגל chrome://flags/#enable-experimental-webassembly-features
ולשלוח לנו משוב.
יכולת פעולה הדדית טובה יותר באינטרנט
WebAssembly יכול להיות נקודת הכניסה לעיבוד נתונים למטרות מיוחדות באינטרנט.
אפשר להשתמש ב-WebAssembly כדי להעביר אפליקציות GPU לאינטרנט. כלומר, אותה אפליקציה ב-C++ שיכולה לפעול במכשיר יכולה לפעול גם באינטרנט, עם שינויים קטנים.
Emscripten, כלי הפיתוח של Wasm, כבר כולל קישורים ל-WebGPU. זו נקודת הכניסה להסקת מסקנות מ-AI באינטרנט, ולכן חשוב מאוד ש-Wasm יוכל לפעול בשילוב חלק עם שאר פלטפורמת האינטרנט. אנחנו עובדים על כמה הצעות שונות בנושא הזה.
שילוב של הבטחה ב-JavaScript (JSPI)
אפליקציות אופייניות בשפות C ו-C++ (וגם בשפות רבות אחרות) נכתבות בדרך כלל מול API סינכרוני. כלומר, האפליקציה תפסיק לפעול עד שהפעולה תושלם. בדרך כלל קל יותר לכתוב אפליקציות חסימה כאלה מאשר אפליקציות עם תמיכה ב-async.
כשפעולות יקרות חוסמות את ה-thread הראשי, הן עלולות לחסום את הקלט/הפלט והתנודות בתנועה יהיו גלויות למשתמשים. יש חוסר התאמה בין מודל התכנות הסינכרוני של אפליקציות מקוריות לבין המודל האסינכרוני של האינטרנט. הבעיה הזו רלוונטית במיוחד לאפליקציות מדור קודם, שההעברה שלהן יקרה. ב-Emscripten יש דרך לעשות זאת באמצעות Asyncify, אבל זו לא תמיד האפשרות הטובה ביותר – הקוד גדול יותר והוא פחות יעיל.
בדוגמה הבאה מחושב רצף פיבונאצ'י באמצעות הבטחות (promises) ב-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 שמחזיר הבטחות (promises).
מידע נוסף על JSPI, על אופן השימוש בו ועל היתרונות שלו זמין במאמר מידע על WebAssembly JavaScript Promise Integration API ב-v8.dev. מידע נוסף על גרסת הבטא הנוכחית למקורות.
בקרת זיכרון
למפתחים יש שליטה קטנה מאוד על הזיכרון של Wasm. המודול הוא הבעלים של הזיכרון שלו. כל ממשקי ה-API שצריכים לגשת לזיכרון הזה צריכים להעתיק נתונים אליו או ממנו, והשימוש הזה יכול להצטבר. לדוגמה, יכול להיות שיהיה צורך באפליקציית גרפיקה להעתיק ולהדפיס מחדש לכל פריים.
ההצעה Memory control נועדה לספק שליטה ברמת פירוט גבוהה יותר על הזיכרון הליניארי של Wasm ולהפחית את מספר העותקים בצינור עיבוד הנתונים של האפליקציה. ההצעה הזו נמצאת בשלב 1. אנחנו יוצרים אב טיפוס שלה ב-V8, מנוע ה-JavaScript של Chrome, כדי להנחות את התפתחות התקן.
איך בוחרים את הקצה העורפי המתאים
מעבדים נפוצים מאוד, אבל הם לא תמיד האפשרות הטובה ביותר. מחשוב למטרות מיוחדות ב-GPU או במאיצי GPU יכול לספק ביצועים טובים פי כמה, במיוחד לדגמים גדולים יותר ולמכשירים מתקדמים. זה נכון גם לגבי אפליקציות שמותאמות למערכת הפעלה ספציפית וגם לגבי אפליקציות אינטרנט.
בחירת הקצה העורפי תלויה באפליקציה, בפלטפורמה או בכלי הפיתוח, וגם בגורמים אחרים שמשפיעים על הביצועים. עם זאת, אנחנו ממשיכים להשקיע בהצעות שמאפשרות ל-Wasm לעבוד היטב עם שאר פלטפורמת האינטרנט, ובאופן ספציפי עם WebGPU.