החלפת נתיב חם ב-JavaScript של האפליקציה שלך באמצעות WebAssembly

הוא מהיר באופן עקבי, יו

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

WebAssembly היא מכונה וירטואלית ברמה נמוכה שמריצה את קוד הבייט ששמור בקובצי .wasm. קוד הבייט הזה מוקלד בצורה חזקה ומובנה בצורה כזאת שהוא יכול להדר ולבצע אופטימיזציה למערכת המארחת מהר הרבה יותר אפשר גם להפעיל JavaScript. WebAssembly מספקת סביבה להרצת קוד שימוש בארגז חול (sandboxing) ובהטמעה כבר מההתחלה.

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

הנתיב החם

ב-squoosh כתבנו פונקציית JavaScript שמסתובבת במאגר תמונות במכפילים של 90 מעלות. אמנם OffscreenCanvas מושלם למטרה הזו, אבל הוא לא נתמך בכל הדפדפנים שבהם טירגטנו, ויש בו באגים מסוימים ב-Chrome.

הפונקציה הזו עוברת באיטרציות על כל פיקסל של תמונת קלט ומעתיקה אותה מיקום שונה בתמונת הפלט כדי להשיג סיבוב. ל-4094 פיקסלים על ברזולוציה של 4096 פיקסלים (16 מגה-פיקסלים), נדרשות יותר מ-16 מיליון איטרציות של בלוק קוד פנימי, שזה מה שאנחנו קוראים לו 'נתיב חם'. למרות מספר החזרות הגדול, שניים מתוך שלושת הדפדפנים שבדקנו סיימו את המשימה תוך 2 שניות או פחות. משך זמן מקובל לאינטראקציה מהסוג הזה.

for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
    for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
    const in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
    outBuffer[i] = inBuffer[in_idx];
    i += 1;
    }
}

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

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

WebAssembly לביצועים צפויים

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

כתיבה ל-WebAssembly

בעבר לקחנו ספריות C/C++ והוספנו אותן ל-WebAssembly כדי להשתמש פונקציונליות באינטרנט. לא נגענו בקוד של הספריות, רק כתבנו כמויות קטנות של קוד C/C++‎ כדי ליצור את הגשר בין הדפדפן לספרייה. הפעם המוטיבציה שלנו שונה: אנחנו רוצים לכתוב משהו מאפס תוך התמקדות ב-WebAssembly, כדי שנוכל לנצל את היתרונות של WebAssembly.

ארכיטקטורת WebAssembly

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

כדי לצטט את WebAssembly.org:

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

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

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

ניהול הזיכרון

בדרך כלל, אחרי שמשתמשים בנפח זיכרון נוסף, צריך לנהל אותו בצורה כלשהי. אילו חלקים מהזיכרון נמצאים בשימוש? אילו שירותים זמינים בחינם? למשל, בגרסה C, הפונקציה malloc(n) מוצאת מקום בזיכרון. של n בייטים ברצף. פונקציות מהסוג הזה נקראות גם "מקצים". כמובן שהיישום של המקצה בשימוש חייב להיכלל ב WebAssembly ולהגדיל את הקובץ. הגודל והביצועים של פונקציות ניהול הזיכרון האלה עשויים להשתנות באופן משמעותי בהתאם לאלגוריתם שבו נעשה שימוש. לכן, בשפות רבות יש כמה הטמעות לבחירה (dmalloc,‏ emmalloc,‏ wee_alloc וכו').

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

יש מבחר גדול

אם בדקתם את פונקציית JavaScript המקורית שאנחנו רוצים לבצע ב-WebAssembly, אפשר לראות שמדובר בפעולה חישובית בלבד ללא ממשקי API ספציפיים ל-JavaScript. לכן, קל יחסית להעביר את הקוד הזה לכל שפה. בדקנו 3 שפות שונות שאפשר להדרש ל-WebAssembly: C/C++,‏ Rust ו-AssemblyScript. השאלה היחידה התשובה לשאלה שלנו בכל אחת מהשפות היא: איך אנחנו ניגשים לזיכרון הגולמי מבלי להשתמש בפונקציות של ניהול זיכרון?

C ו-Emscripten

Emscripten הוא מהדר C ליעד WebAssembly. המטרה של Emscripten היא לשמש כתחליף ללא תהליך העברה למהדרי C ידועים כמו GCC או clang, והוא תואם ברוב הדגלים. זהו חלק מרכזי במשימה של Emscripten, שמטרתה להפוך את הידור הקוד הקיים ב-C וב-C++ ל-WebAssembly לפשוט ככל האפשר.

הגישה לזיכרון גולמי היא חלק מהטבע של C, ומסיבה זו בדיוק קיימים מצביעים:

uint8_t* ptr = (uint8_t*)0x124;
ptr[0] = 0xFF;

כאן אנחנו הופכים את המספר 0x124 למצביע למספרים שלמים (או ביטים) לא חתומים באורך 8 ביט. הפעולה הזו הופכת את המשתנה ptr למערך שמתחיל בכתובת הזיכרון 0x124, שאפשר להשתמש בו כמו בכל מערך אחר, ומאפשרת לנו לגשת ביטים ספציפיים לצורך קריאה וכתיבה. במקרה שלנו, אנחנו מדברים על מאגר RGBA של תמונה שרוצים לשנות את הסדר שלה כדי לבצע סיבוב. כדי להזיז פיקסל, עלינו להעביר 4 בייטים עוקבים בו-זמנית (בייט אחד לכל ערוץ: R, G, B ו-A). כדי לעשות זאת בקלות, אפשר ליצור מערך של מספרים שלמים 32-ביט ללא סימן. לפי הסכמה, תמונה הקלט תתחיל בכתובת 4 ותמונת הפלט תתחיל מיד אחרי שתמונת הקלט תסתיים:

int bpp = 4;
int imageSize = inputWidth * inputHeight * bpp;
uint32_t* inBuffer = (uint32_t*) 4;
uint32_t* outBuffer = (uint32_t*) (inBuffer + imageSize);

for (int d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
    for (int d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
    int in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
    outBuffer[i] = inBuffer[in_idx];
    i += 1;
    }
}

אחרי שמעבירים את פונקציית JavaScript כולה ל-C, אנחנו יכולים להדר את קובץ ה-C עם emcc:

$ emcc -O3 -s ALLOW_MEMORY_GROWTH=1 -o c.js rotate.c

כמו תמיד, emscripten יוצר קובץ של קוד דבק שנקרא c.js ומודול wasm שנקרא c.wasm. הערה: קובץ ה-gzip של מודול ה-wasm הוא רק כ-260 בייטים, בעוד שקוד הדבקה הוא כ-3.5KB אחרי gzip. אחרי קצת כישורים, הצלחנו לוותר את קוד החיבור וליצור מודולים של WebAssembly באמצעות ממשקי ה-API של וניל. בדרך כלל אפשר לעשות זאת באמצעות Emscripten, כל עוד לא משתמשים בספרייה הרגילה של C‎.

Rust

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

אחד מהכלים האלה הוא wasm-pack, של קבוצת העבודה rustwasm. wasm-pack לוקח את הקוד והופך אותו למודול ידידותי לאינטרנט שפועל חדשני עם חבילות כמו webpack. wasm-pack הוא במיוחד נוח, אבל כרגע פועל רק עבור חלודה. הקבוצה שוקלת להוסיף תמיכה בשפות אחרות שמטרגטות WebAssembly.

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

let imageSize = (inputWidth * inputHeight) as usize;
let inBuffer: &mut [u32];
let outBuffer: &mut [u32];
unsafe {
    inBuffer = slice::from_raw_parts_mut::<u32>(4 as *mut u32, imageSize);
    outBuffer = slice::from_raw_parts_mut::<u32>((imageSize * 4 + 4) as *mut u32, imageSize);
}

for d2 in 0..d2Limit {
    for d1 in 0..d1Limit {
    let in_idx = (d1Start + d1 * d1Advance) * d1Multiplier + (d2Start + d2 * d2Advance) * d2Multiplier;
    outBuffer[i as usize] = inBuffer[in_idx as usize];
    i += 1;
    }
}

מתבצע הידור של קובצי חלודה באמצעות

$ wasm-pack build

יוצר מודול wasm בגודל 7.6KB עם כ-100 בייטים של קוד דבק (שניהם אחרי gzip).

AssemblyScript

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

    for (let d2 = d2Start; d2 >= 0 && d2 < d2Limit; d2 += d2Advance) {
      for (let d1 = d1Start; d1 >= 0 && d1 < d1Limit; d1 += d1Advance) {
        let in_idx = ((d1 * d1Multiplier) + (d2 * d2Multiplier));
        store<u32>(offset + i * 4 + 4, load<u32>(in_idx * 4 + 4));
        i += 1;
      }
    }

בהתחשב בסוג פני השטח הקטן של הפונקציה rotate(), לנייד את הקוד הזה בקלות ל-AssemblyScript. הפונקציות load<T>(ptr: usize) ו-store<T>(ptr: usize, value: T) הן פונקציות של AssemblyScript שמאפשרות לגשת לזיכרון גולמי. כדי להדר את קובץ AssemblyScript, צריך רק להתקין את חבילת ה-npm AssemblyScript/assemblyscript ולהריץ את הפקודה

$ asc rotate.ts -b assemblyscript.wasm --validate -O3

מערכת AssemblyScript תספק לנו מודול Wasm בנפח של כ-300 בייטים וללא קוד דבק. המודול עובד רק עם ממשקי API של וניל WebAssembly.

WebAssembly Forensics

7.6KB של Rust מפתיעים את הגודל בהשוואה לשתי השפות האחרות. יש יש כמה כלים בסביבה העסקית של WebAssembly שיכולים לעזור לכם לנתח קובצי WebAssembly (ללא קשר לשפה שבה נוצרו) וגם לספר לכם מה קורה, וגם לעזור לכם לשפר את המצב.

טוויגי

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

$ twiggy top rotate_bg.wasm
צילום מסך של התקנת Twiggy

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

וואטס-סטריפ

wasm-strip הוא כלי מ-WebAssembly Binary Toolkit, או בקיצור WAB. הוא מכיל כמה כלים שמאפשרים לבדוק מודולים של WebAssembly ולשנות אותם. wasm2wat הוא מנתח קוד שממיר מודול wasm בינארי לפורמט קריא לבני אדם. קובץ ה-Wabt מכיל גם wat2wasm, שמאפשר להפנות בפורמט קריא לאנשים חזרה למודול Wam בינארי. אמנם השתמשנו בשני הכלים המשולבים האלה כדי לבדוק את קובצי WebAssembly שלנו, אבל גילינו ש-wasm-strip הוא הכלי הכי שימושי. הקטעים המיותרים יוסרו על ידי wasm-strip ואת המטא-נתונים ממודול WebAssembly:

$ wasm-strip rotate_bg.wasm

כך גודל הקובץ של מודול החלודה יקטן מ-7.5KB ל-6.6KB (לאחר gzip).

wasm-opt

wasm-opt הוא כלי מ-Binaryen. הוא לוקח מודול WebAssembly ומנסה לבצע אופטימיזציה שלו גם מבחינת גודל וגם על סמך קוד בייט בלבד. כלים מסוימים, כמו Emscripten כבר מופעלים את הכלי הזה, ויש כאלה שלא. בדרך כלל כדאי לנסות ולחסוך בייטים נוספים באמצעות הכלים האלה.

wasm-opt -O3 -o rotate_bg_opt.wasm rotate_bg.wasm

בעזרת wasm-opt אפשר לחסוך עוד כמה בייטים, כך שיישארו בסך 6.2KB אחרי gzip.

#![no_std]

לאחר התייעצות ומחקר, שכתבנו את קוד החלודה שלנו בלי להשתמש הספרייה הרגילה של Rust באמצעות #![no_std] . הפעולה הזו משביתה גם את הקצאות הזיכרון הדינמיות לגמרי, ומסירה את הקוד של מנהל ההקצאות מהמודול שלנו. מתבצע הידור של קובץ ה-Rust הזה עם

$ rustc --target=wasm32-unknown-unknown -C opt-level=3 -o rust.wasm rotate.rs

הניב מודול Wam של 1.6KB אחרי wasm-opt, wasm-strip ו-gzip. הוא עדיין גדול יותר מהמודולים שנוצרים על ידי C ו-AssemblyScript, אבל הוא קטן מספיק כדי להיחשב לקל.

ביצועים

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

איך מבצעים השוואה לשוק

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

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

השוואת ביצועים

השוואת מהירות לפי שפה
השוואת מהירות לכל דפדפן

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

מבלי לנתח את התרשימים יותר מדי, ברור שפתרנו את הבעיה המקורית בעיית ביצועים: כל המודולים של WebAssembly פועלים תוך כ-500 אלפיות השנייה. התוצאות האלה מאשרות את מה שציינו בהתחלה: WebAssembly מספק ביצועים חזויים. לא משנה באיזו שפה אנו בוחרים, ההבדלים בין הדפדפנים ושפות הוא מינימלי. ליתר דיוק: סטיית התקן של JavaScript בכל הדפדפנים היא בערך 400 אלפיות השנייה, בעוד שסטיית התקן של כל מודולים של WebAssembly בכל הדפדפנים הם באורך של כ-80 אלפיות שנייה.

השקעת מאמץ

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

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

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

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

סיכום

אז באיזו שפה להשתמש אם יש לכם נתיב חם של JS ואתם רוצים לבצע אותו? מהיר או עקבי יותר עם WebAssembly. כמו תמיד עם ביצועים התשובה היא: תלוי. מה שלחנו?

תרשים השוואה

השוואת גודל המודול / שילוב הביצועים של השפות השונות שבה השתמשנו, הבחירה הטובה ביותר היא C או AssemblyScript. החלטנו לשלוח את Rust. יש יש כמה סיבות להחלטה הזו: כל רכיבי הקודק שנשלחו ב-Squoosh עד עכשיו עוברים הידור באמצעות Emscripten. רצינו להרחיב את הידע שלנו על בסביבה העסקית של WebAssembly ובשפה אחרת בייצור. AssemblyScript הוא חלופה חזקה, אבל הפרויקט צעיר יחסית המהדר לא בוגר כמו המהדר של Rust.

ההבדל בגודל הקובץ בין Rust לשפות האחרות נראה דרסטי מאוד בתרשים הפזור, אבל הוא לא כזה גדול במציאות: טעינת 500B או 1.6KB, גם מעל 2GB, נמשכת פחות מעשירית שנייה. וגם חלודה תסגור בקרוב את הפער מבחינת גודל המודול.

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

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

עדכון: Rust

אחרי פרסום המאמר, ניק פיצג'רלד מצוות Rust הפנה אותנו אל הספר המעולה שלו, Rust Wasm, קטע שעוסק באופטימיזציה של גודל קובץ. ביצוע הפעולות הבאות: הוראות להתאמה אישית (בעיקר פעילות של אופטימיזציה של זמן קישור טיפול בפחדות) אפשר לנו לכתוב קוד חלודה "רגיל" ולחזור להשתמש Cargo (npm של חלודה) בלי להגדיל את גודל הקובץ. אחרי ה-gzip, מודול Rust מסתכם ב-370B. לפרטים נוספים, אפשר לעיין ביחס גובה-רוחב שפתחתי ב-Squoosh.

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