Thread Affinity - איך מצמידים תהליכים לליבות בצורה חכמה
Thread Affinity - איך מצמידים תהליכים לליבות בצורה חכמה
בפוסט הקודם ראינו למה מעבר thread בין ליבות עולה ביצועים. עכשיו נצלול לשאלה המעשית: איך עושים הצמדה נכון?
מה זה Thread Affinity?
Thread Affinity הוא הצמדה של thread לליבה קבועה או לקבוצת ליבות מוגדרת, כך שמערכת ההפעלה לא מעבירה אותו בין ליבות. זה מונע context switching מיותר, שומר על יציבות ה-cache, ומאפשר למעבד לעבוד ברצף בלי “הפרעות”.
איך זה עובד מאחורי הקלעים?
בכל מעבד יש כמה ליבות פיזיות, וכל אחת מהן חולקת משאבים מסוימים - כמו cache, בקרי זיכרון, ו-NUMA nodes (נושא שנעמיק בו בהמשך). כשthread “קופץ” לליבה אחרת, הוא מאבד את כל היתרונות המקומיים האלה.
כשקובעים affinity, בעצם אומרים למערכת:
“אל תיגעי בזה - ה-thread הזה שייך לליבה הזו.”
בפועל, זה נעשה על ידי מיפוי בין threads לליבות, בדרך כלל דרך קונפיגורציה או ספרייה ייעודית (למשל taskset בלינוקס, או הגדרות בסביבת ה-runtime של מנוע ה-inference).
מתי זה קריטי במיוחד?
- כשמריצים מודלים כבדים (כמו מודלי Transformer) שמבצעים חישובים מרובי-שלבים על אותם נתונים.
- כשיש הרבה בקשות במקביל (concurrency גבוה) - כמו שרת inference שמשרת מאות משתמשים.
- במערכות עם כמה NUMA nodes - שם מעבר בין אזורי זיכרון שונים יכול להשפיע דרמטית על זמן התגובה.
איך מחליטים מה מצמידים למה?
- מפרידים threads קריטיים מ-threads תומכים: לדוגמה, ליבות מסוימות מוקצות לחישוב, בעוד אחרות מטפלות בתקשורת (I/O).
- משתמשים בקבוצות ליבות: אם יש 32 ליבות, אפשר להגדיר קבוצות של 4-8 ליבות למודל, בהתאם לעומס.
- בודקים באופן איטרטיבי: מתחילים מהצמדה בסיסית - ואז מודדים latency ו-throughput. כל שינוי קטן בהקצאה יכול להשפיע משמעותית על הביצועים.
טיפ מקצועי
לפעמים אופטימיזציה של Thread Affinity לא תשפר ביצועים - ואפילו עלולה לפגוע בהם, אם היא יוצרת צפיפות (יותר מדי threads על פחות מדי ליבות). לכן תמיד שווה למדוד בפועל - אין תחליף ל-benchmarking מדויק.
בשורה התחתונה
Thread Affinity הוא כלי פשוט אך עוצמתי: הוא לא דורש קוד חדש, רק ניהול נכון של המשאבים הקיימים. במערכת שבה כל מילישנייה נחשבת, שליטה במקום שבו כל thread רץ יכולה להיות ההבדל בין מודל ש”עובד” למודל ש”עף”.
📚 פוסטים נוספים בסדרה: אופטימיזציה של חומרת הסקה
- חלק 1 למה בכלל צריך להבין חומרה כשעוסקים באופטימיזציות Inference?
- חלק 2 מה זה NUMA ולמה זה חשוב באופטימיזציות Inference?
- חלק 3 מה זה ליבות (Cores) ו-Threads?
- חלק 4 מה זה Cache ולמה הוא משנה הכל?
- חלק 5 חלוקת ליבות - איך לנהל נכון את כוח העיבוד שלך
- חלק 7 Divided Resources - איך מחלקים משאבים בין מודלים או תהליכים
- חלק 8 Resource Optimization - איך כל הגורמים משפיעים בפועל על Latency ו-TPS
- חלק 9 סיכום הסדרה: מ-NUMA ועד Throughput - איך אופטימיזציה הופכת חומרה לביצועים