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

כל משימת הסקה (inference) של מודל שפה גדול תלויה בשני מאפיינים פיזיים של ה-GPU היושב בשרת שלכם: כמה מהר הוא יכול לעבד מספרים, וכמה מהר הוא יכול להזיז את המספרים הללו למקום כדי שיוכלו לעבור עיבוד.

המתמטיקה זולה. הזזת הנתונים לא

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

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

ב-GPUs מודרניים של מרכזי נתונים, היחידות האריתמטיות כל כך חזקות שהן מסיימות לעיתים קרובות את החישובים שלהן ואז יושבות ללא מעש, מבזבזות מחזורי שעון בזמן שהן מחכות שמשקלים ואקטיב

Quantization תוקפת ישירות את בעיית רוחב הפס. משקולות המודל נשמרות בדרך כלל בפורמטים של נקודה צפה ב-16 סיביות (sixteen-bit floating-point). על ידי דחיסתן למספרים שלמים של 8 סיביות או אפילו 4 סיביות, אתם פשוט מקצצים בחצי או יותר מכמות הנתונים שעוברת בבאס. המודל עדיין זקוק לדיוק מספיק כדי להפיק פלט קוהרנטי, אך שיטות מודרניות של post-training quantization יכולות לצמצם משמעותית את צריכת הזיכרון של המודל מבלי להרוס את האיכות. פחות נתונים בנתיב הנתונים (in flight) משמעו פחות זמן המתנה בבקר הזיכרון.

FlashAttention מבנה מחדש את מנגנון ה-attention כדי לשמור על תוצאות ביניים בתוך הזיכרון המהיר של ה-GPU (on-chip memory). מנגנון attention סטנדרטי נאלץ לכתוב מטריצות attention גדולות לזיכרון חיצוני איטי ואז לקרוא אותן בחזרה. FlashAttention מפרקת את החישוב ל"אריחים" (tiles) קטנים יותר שנכנסים ל-SRAM, מבצעת את שלבי ה-softmax וה-scaling על השבב, וכותבת רק את הפלטים הסופיים בחזרה לזיכרון בעל רוחב פס גבוה. היא מחליפה מעט חישוב נוסף במספר קטן בהרבה של סבבי קריאה-כתיבה לזיכרון הראשי, מה שמעט כמעט תמיד מהווה הימור מנצח.

PagedAttention פותרת סוג אחר של בזבוז זיכרון. במהלך ה-decode, ה-KV cache גדל בצורה בלתי צפויה. מערכות מסורתיות מקצות בלוקים רציפים וקבועים של זיכרון לכל רצף (sequence), מה שמשאיר "חורים" גדולים כאשר חלק מהרצפים מסתיימים מוקדם ואחרים מתרחבים. PagedAttention שואבת את המושג של זיכרון וירטואלי ממערכות הפעלה. היא שומרת רשומות KV cache בבלוקים בגודל קבוע שניתן להקצות באופן לא רציף ולמפות באמצעות טבלת עקיפה (indirection table). זה מונע מהזיכרון לשבת ללא שימוש בתוך באפרים שמורים אך חצי-ריקים, ומאפשר גדלי batch גדולים יותר, מה שבתורו משפר את התפוקה (throughput) הכללית על ידי שמירה על באס הזיכרון עסוק בעבודה מועילה במקום בעומס של פרגמנטציה.

לשנות את השאלה

כאשר יש קפיצות בשיהוי (latency), יותר מדי צוותים שואלים האם עליהם לעבור למודל קטן יותר או לכתוב מחדש את שרת ההסקה (inference server) שלהם. השאלות הללו חשובות, אך הן משניות. השאלה הראשונה צריכה להיות לגבי החומרה עצמה. האם ה-GPU שלכם באמת עסוק בחישוב, או שהוא "רעב" לנתונים?

הסתכלו על מדדי הניצול (utilization) שלכם. בצעו פרופיל (profile) של רוחב פס הזיכרון לצד תפוסת החישוב של ה-GPU. אם אתם רואים תחרות גבוהה על הזיכרון (memory contention) ועצימות אריתמטית (arithmetic intensity) נמוכה במהלך ה-decode, אין לכם בעיה של ארכיטקטורת מודל. יש לכם בעיית פיזיקה. הפתרון לא יגיע מ-Python נקי יותר. הוא יגיע מביצוע batching אגרסיבי יותר, קוונטיזציה של המשקולות כדי לדחוף אותן דרך הצינור מהר יותר, ארגון מחדש של ה-attention כדי להישאר על השבב, וניהול ה-KV cache כך שתוכלו להכניס batches גדולים יותר מבלי להיגמר מהמקום.

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