משפרים את יעילות הדחיסה עם מילונים משותפים

תאריך פרסום: 6 במרץ 2024

דחיסת נתונים היא טכניקה מוכחת לשיפור הביצועים שמקטינה את הגודל של משאבי דפים שעומדים בדרישות. במשך תקופה מסוימת, היה נהוג להשתמש בעיקר ב-gzip בשרתי אינטרנט כדי לדחוס משאבים נפוצים של דפים מבוססי-טקסט, כמו קובצי HTML, ‏ CSS ו-JavaScript, ולשלוח אותם ללקוח כדי שיוכל לבצע להם דה-קומפרסיה. התוצאה היא זמני טעינה מהירים יותר של משאבים בלי להשפיע על ההתנהגות המיועדת של דף.

למרות ש-gzip יעיל מאוד בפני עצמו, בשנים האחרונות חלו שיפורים נוספים בדחיסה באינטרנט. בשנת 2016, אלגוריתם Brotli נכלל ב-Chrome, והוא מספק יחסי דחיסה טובים יותר באופן כללי למשאבים שעומדים בדרישות. עד סוף 2017, כל הדפדפנים המודרניים תמכו ב-Brotli, והתמיכה בו בשרתים החלה להתפשט. לאחרונה, הוספה ל-Chrome דחיסה באמצעות ZStandard.

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

הסבר על מילונים משותפים

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

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

הנה דוגמה שממחישה את היעילות של מילון דחיסה בהתאמה אישית: נניח שהאתר שלכם משתמש ב-Angular framework, והגרסה הנוכחית שבה אתם משתמשים היא גרסה 1.7.9. הגרסה הזו של Angular framework היא בנפח של כ-172KiB לא דחוס. כשדוחסים אותו באמצעות הגדרות ברירת המחדל של Brotli, הגודל שלו הופך לכ-53KB. כך מתקבל יחס דחיסה של כמעט 70%. עם זאת, נניח שבהמשך תחליטו לשדרג ל-Angular 1.8.3. בהתחשב בכך שהגודל של הגרסה הזו של Angular דומה בערך לגודל של גרסה 1.7.9, אפשר לצפות ליחס דחיסה כמעט זהה לזה של הגרסה הקודמת.

כאן מילון מותאם אישית יכול להיות שימושי. הוא מאפשר להשתמש בתהליך שנקרא דחיסת דלתא , שבו אפשר להשתמש במילון של גרסה קודמת של משאב כדי לדחוס גרסה מאוחרת יותר. בדוגמה הקודמת, אם דחסתם את גרסה 1.8.3 של Angular באמצעות גרסה 1.7.9 כמילון, הפלט יהיה קצת יותר מ-4KiB. זהו יחס דחיסה של כמעט 98%. ברור שמילוני דחיסה יכולים להשפיע מאוד על ביצועי הטעינה, והיעילות שלהם כבר הוכחה ביישומים בעולם האמיתי.

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

איך Chrome מפרסם תמיכה במילונים משותפים

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

Accept-Encoding: gzip, br, zstd

הכותרת הספציפית הזו Accept-Encoding מציינת שהדפדפן ששולח את הבקשה למשאב תומך באלגוריתמים לדחיסה gzip,‏ Brotli ו-ZStandard. שרת אינטרנט שמגיב לבקשה יכול להחליט באיזה אלגוריתם להשתמש בתגובה לבקשה.

כשהתמיכה במילון משותף מופעלת ומילון רלוונטי זמין למשאב, נוספים טוקנים נוספים לכותרת Accept-Encoding. הטוקנים האלה הם br-d עבור Brotli ו-zstd-d עבור Zstandard. בנוסף, Chrome יכלול את הגיבוב של מילון זמין, שיוסבר בהמשך.

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

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

דחיסה של מילון משותף למשאבים סטטיים

משאב של דף סטטי הוא משאב שתמיד מפיק את אותה תגובה לכתובת URL מבוקשת. דוגמאות נפוצות למשאבים של דפים סטטיים שאפשר לדחוס הם קובצי JavaScript ו-CSS. בדרך כלל יש גרסאות של המשאבים האלה למטרות שמירה במטמון – לפעמים עם גיבוב של תוכן הקובץ בשם הקובץ (לדוגמה styles.abcd1234.css) או בשיטה אחרת של טביעת אצבע של המשאב. סוגי המשאבים האלה הם מועמדים מצוינים לדחיסת דלתא שמסופקת על ידי מילונים משותפים, כי משאבים סטטיים נשמרים לעיתים קרובות במטמון לתקופות ארוכות, ויש להם נטייה להתעדכן בתדירות מסוימת.

אפשר לציין מילון למשאב סטטי על ידי הגדרת כותרת התגובה Use-As-Dictionary בשבילו. הכותרת מקבלת אחד מכמה צמדי מפתח/ערך, אבל הצמד היחיד שחובה להשתמש בו הוא match, שמקבל תחביר URLPattern שמציין את נתיב המשאב שבו צריך להשתמש במילון:

Use-As-Dictionary: match="/dist/styles.*.css"

אפשר לחשוב על הכותרת Use-As-Dictionary כמנגנון שחל על גרסאות עתידיות של משאב שתואמות לדפוס שצוין בה. לדוגמה, נניח שהאתר שלכם שולח את כל הסגנונות שלו בקובץ CSS יחיד. לצורך הפשטות, נניח שהגרסה הראשונה של המשאב הזה נמצאת בכתובת /dist/styles.v1.css, ונשלחת עם כותרת תגובה Use-As-Dictionary שמכילה ערך match של /dist/styles.*.css.

אחרי זמן מה, אתם מעדכנים את ה-CSS של האתר ושולחים גרסה חדשה שלו בכתובת /dist/styles.v2.css. מכיוון שהערך match שבו נעשה שימוש בכותרת התגובה Use-As-Dictionary מהגרסה הקודמת חל על הבקשה הזו, הדפדפן ישלח כותרת Available-Dictionary שמכילה גיבוב של המילון שמקודד כרצף בייטים של שדה מובנה:

Accept-Encoding: gzip, br, zstd, br-d, zstd-d
Available-Dictionary: :pZGm1Av0IEBKARczz7exkNYsZb8LzaMrV7J32a2fFG4=:

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

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

דחיסה של מילון משותף למשאבים דינמיים

גם משאבים דינמיים יכולים להפיק תועלת מדחיסה של מילון משותף. משאבים דינמיים הם משאבים שמשתנים בהתאם להקשר – למשל, אתר חדשות שבו הדף הראשי מתעדכן לעיתים קרובות כשיש חדשות. מסמכי HTML הם לרוב משאבים דינמיים. במקרים כאלה, המילון יכול להכיל את רוב מבנה ה-HTML הנפוץ של האתר ואת קוד התבנית, וכך להוביל לדפים דחוסים שבהם נשלחים רק החלקים הייחודיים של כל דף.

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

<link rel="dictionary" href="/dictionary.dat">

כש-Chrome נתקל ברכיב <link> הזה, הוא עשוי לאחזר את המילון אחרי שהדף בלי פעילות, ובעדיפות נמוכה כדי למנוע תחרות על רוחב הפס. בתגובה למילון עצמו צריך לציין כותרת Use-As-Dictionary ולציין את נתיב המשאב הדינמי שאליו היא מתייחסת:

Use-As-Dictionary: match="/product/*"

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

דחיסת משאבים סטטיים בזמן הבנייה

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

רוב כלי האריזה (bundlers) שמבוססים על Node.js ותומכים בדחיסה משתמשים בספריית Zlib המובנית של Node. ‫Zlib תומך ב-Brotli, ובדרך כלל חבילות שמשתמשות בו מציעות ממשק להעברת אפשרויות ישירות אל Zlib, שתומך בדחיסה בעזרת מילון. הנה כמה כלי חבילה שתומכים בשימוש במילונים:

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

כדאי לנסות!

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

  1. אם אתם רוצים להתנסות בדחיסה של מילון משותף בעצמכם כדי להבין איך היא פועלת, אתם יכולים להפעיל את תכונת הניסוי העברה של מילון דחיסה בדף chrome://flags.
  2. אם אתם רוצים לנסות את התכונה הזו באתר הייצור שלכם ולראות איך דחיסה של מילון משותף יכולה להועיל למשתמשים אמיתיים, אתם יכולים להירשם לניסוי המקור כדי לקבל טוקן, ולקרוא על אופן הפעולה של ניסויי מקור.

סיכום

אנחנו מאוד מתרגשים מההתקדמות המשמעותית הזו בטכנולוגיית הדחיסה באינטרנט, ומהאפשרות שהיא תאפשר להריץ מהר יותר אפליקציות קיימות שאנשים משתמשים בהן מדי יום. אנחנו ממליצים לכם לנסות את התכונה, והכי חשוב, נשמח לשמוע מה דעתכם אם תנסו אותה! אם מצאתם באג, דווחו עליו בכתובת crbug.com. כדי לקבל משאבים וכלים נוספים, אפשר להיכנס לכתובת use-as-dictionary.com. לבסוף, אם אתם רוצים לקבל הסבר מפורט על אופן הפעולה של התוסף, ההסבר הוא השלב הבא המומלץ.