מטב את ביצועי TensorFlow GPU עם TensorFlow Profiler

סקירה כללית

מדריך זה יראה לך כיצד להשתמש ב-TensorFlow Profiler עם TensorBoard כדי לקבל תובנות ולהפיק את הביצועים המקסימליים מה-GPU שלך, ולבצע ניפוי באגים כאשר אחד או יותר מה-GPU שלך אינם מנוצלים.

אם אתה חדש ב-Profiler:

זכור כי הורדת חישובים ל-GPU לא תמיד תהיה מועילה, במיוחד עבור דגמים קטנים. יכול להיות תקורה עקב:

  • העברת נתונים בין המארח (CPU) למכשיר (GPU); ו
  • בשל השהיה הכרוכה כאשר המארח משיק גרעיני GPU.

זרימת עבודה של מיטוב ביצועים

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

מומלץ לנפות באגים בבעיות ביצועים בסדר הבא:

  1. מטב וניפוי באגים בביצועים ב-GPU אחד:
    1. בדוק אם צינור הקלט הוא צוואר בקבוק.
    2. איתור באגים בביצועים של GPU אחד.
    3. אפשר דיוק מעורב (עם fp16 (float16)) ובאופן אופציונלי אפשר את XLA .
  2. מטב וניפוי באגים בביצועים במארח יחיד מרובה-GPU.

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

בתור בסיס לקבלת קוד ביצועים במעבדי GPU, מדריך זה מניח שאתה כבר משתמש tf.function . ממשקי API של Keras Model.compile ו- Model.fit ישתמשו tf.function באופן אוטומטי מתחת למכסה המנוע. בעת כתיבת לולאת אימון מותאמת אישית עם tf.GradientTape , עיין בפונקציה Better Performance with tf.function כיצד להפעיל את tf.function s.

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

1. מטב את הביצועים ב-GPU אחד

במקרה אידיאלי, לתוכנית שלך צריכה להיות ניצול GPU גבוה, תקשורת מינימלית בין CPU (המארח) ל-GPU (המכשיר), וללא תקורה מצינור הקלט.

השלב הראשון בניתוח הביצועים הוא לקבל פרופיל עבור דגם הפועל עם GPU אחד.

דף סקירת הפרופילים של TensorBoard — המציג תצוגה ברמה העליונה של ביצועי המודל שלך במהלך ריצת פרופיל — יכול לספק מושג עד כמה התוכנית שלך רחוקה מהתרחיש האידיאלי.

TensorFlow Profiler Overview Page

המספרים העיקריים שיש לשים לב לדף הסקירה הם:

  1. כמה מזמן הצעד הוא מביצוע המכשיר בפועל
  2. אחוז הפעולות שבוצעו במכשיר לעומת המארח
  3. כמה גרעינים משתמשים fp16

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

להלן תמונה של תצוגת מעקב דגם הפועלת על GPU אחד. מהקטעים של TensorFlow Name Scope ו- TensorFlow Ops , אתה יכול לזהות חלקים שונים של המודל, כמו המעבר קדימה, פונקציית ההפסד, חישוב מעבר אחורה/שיפוע ועדכון משקל המיטוב. אתה יכול גם להפעיל את האופציות על ה-GPU ליד כל זרם , המתייחסים לזרמי CUDA. כל זרם משמש למשימות ספציפיות. במעקב זה, Stream#118 משמש להפעלת גרעיני מחשוב ועותקי מכשיר למכשיר. Stream#119 משמש להעתקה מארח למכשיר ו- Stream#120 להעתקה של מכשיר לארח.

העקיבה שלהלן מציגה מאפיינים נפוצים של מודל בעל ביצועים.

image

לדוגמה, ציר הזמן של חישוב ה-GPU ( Stream#118 ) נראה "עסוק" עם מעט מאוד פערים. ישנם עותקים מינימליים ממארח ​​למכשיר ( זרם #119 ) וממכשיר למארח ( זרם #120 ), כמו גם פערים מינימליים בין השלבים. כאשר אתה מפעיל את הפרופיל עבור התוכנית שלך, ייתכן שלא תוכל לזהות את המאפיינים האידיאליים הללו בתצוגת המעקב שלך. שאר המדריך הזה מכסה תרחישים נפוצים וכיצד לתקן אותם.

1. ניפוי באגים בצינור הקלט

השלב הראשון באיתור ביצועי GPU הוא לקבוע אם התוכנית שלך קשורה לקלט. הדרך הקלה ביותר להבין זאת היא להשתמש ב-Profiler's Input-pipeline Analyser , ב-TensorBoard, המספק סקירה כללית של הזמן המושקע בצינור הקלט.

image

אתה יכול לבצע את הפעולות הפוטנציאליות הבאות אם צינור הקלט שלך תורם משמעותית לזמן הצעד:

  • אתה יכול להשתמש במדריך הספציפי tf.data כדי ללמוד כיצד לנפות באגים בצינור הקלט שלך.
  • דרך מהירה נוספת לבדוק אם צינור הקלט הוא צוואר הבקבוק היא להשתמש בנתוני קלט שנוצרו באופן אקראי שאינם זקוקים לעיבוד מקדים. הנה דוגמה לשימוש בטכניקה זו עבור מודל ResNet. אם צינור הקלט אופטימלי, אתה אמור לחוות ביצועים דומים עם נתונים אמיתיים ועם נתונים אקראיים/סינטטיים שנוצרו. התקורה היחידה במקרה של נתונים סינתטיים תהיה עקב העתקת נתוני קלט אשר שוב ניתן לשלוף מראש ולמטב.

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

2. ניפוי באגים בביצועים של GPU אחד

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

1. נתח פערים בין השלבים

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

image

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

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

אם אתה מבחין בפערים גדולים בצד המארח, שמתזמן פעולות אלה ב-GPU, אתה יכול להגדיר את משתנה הסביבה TF_GPU_THREAD_MODE=gpu_private . זה מבטיח שגרעיני GPU יושקו מהשרשורים הייעודיים שלהם, ולא יעמדו בתור מאחורי עבודת tf.data .

פערים בין שלבים יכולים להיגרם גם מחישובים מדדים, התקשרויות חוזרות של Keras או פעולות מחוץ ל- tf.function שפועלות על המארח. לאופציות האלה אין ביצועים טובים כמו לאופציות בתוך גרף TensorFlow. בנוסף, חלק מהאופציות הללו פועלות על המעבד ומעתיקות טנסורים הלוך ושוב מה-GPU.

אם לאחר ביצוע אופטימיזציה של צינור הקלט שלך אתה עדיין מבחין בפערים בין השלבים במציג המעקב, עליך להסתכל על קוד המודל בין השלבים ולבדוק אם השבתת החזרות/מדדים משפרת את הביצועים. חלק מהפרטים של פעולות אלה נמצאים גם במציג המעקב (הן בצד המכשיר והן בצד המארח). ההמלצה בתרחיש זה היא להפחית את התקורה של פעולות אלה על ידי ביצוען לאחר מספר קבוע של שלבים במקום כל שלב. בעת שימוש בשיטת Model.compile ב-API tf.keras , הגדרת הדגל steps_per_execution עושה זאת באופן אוטומטי. עבור לולאות אימון מותאמות אישית, השתמש tf.while_loop .

2. השג ניצול גבוה יותר של המכשיר

1. גרעיני GPU קטנים ועיכובים בהשקת ליבת המארח

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

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

image

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

image

על ידי השקת הרבה אופציות קטנות ב-GPU (כמו תוספת סקלרית, למשל), ייתכן שהמארח לא יעמוד בקצב של ה-GPU. הכלי TensorFlow Stats ב-TensorBoard עבור אותו פרופיל מציג 126,224 פעולות Mul הנמשכות 2.77 שניות. לפיכך, כל ליבה היא בערך 21.9 μs, וזה קטן מאוד (בערך באותו זמן כמו זמן ההשהיה של ההשקה) ועלול לגרום לעיכובים בהשקה של ליבת המארח.

image

אם מציג המעקב שלך מראה פערים קטנים רבים בין פעולות ב-GPU כמו בתמונה למעלה, אתה יכול:

  • שרשור טנזורים קטנים והשתמש באופציות וקטוריות או השתמש בגודל אצווה גדול יותר כדי לגרום לכל ליבה שהושקה לעשות יותר עבודה, מה שישאיר את ה-GPU עסוק למשך זמן רב יותר.
  • ודא שאתה משתמש tf.function כדי ליצור גרפים של TensorFlow, כדי שלא תפעיל אופציות במצב להוט טהור. אם אתה משתמש Model.fit (בניגוד ללולאת אימון מותאמת אישית עם tf.GradientTape ), אז tf.keras.Model.compile יעשה זאת עבורך באופן אוטומטי.
  • נתיך גרעינים באמצעות XLA עם tf.function(jit_compile=True) או אשכולות אוטומטיים. לפרטים נוספים, עבור לסעיף אפשר דיוק מעורב ו-XLA למטה כדי ללמוד כיצד להפעיל את XLA כדי לקבל ביצועים גבוהים יותר. תכונה זו יכולה להוביל לניצול גבוה של המכשיר.
2. מיקום TensorFlow op

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

image

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

כדי לגלות לאילו מכשירים מוקצים הפעולות והטנזורים בדגם שלך, הגדר את tf.debugging.set_log_device_placement(True) כהצהרה הראשונה של התוכנית שלך.

שים לב שבמקרים מסוימים, גם אם אתה מציין אופציה להצבה במכשיר מסוים, היישום שלו עשוי לעקוף את התנאי הזה (לדוגמה: tf.unique ). אפילו עבור אימון GPU יחיד, ציון אסטרטגיית הפצה, כגון tf.distribute.OneDeviceStrategy , יכול לגרום למיקום דטרמיניסטי יותר של פעולות במכשיר שלך.

סיבה אחת לכך שרוב האופציות ממוקמות ב-GPU היא למנוע עותקי זיכרון מוגזמים בין המארח למכשיר (צפויים עותקי זיכרון עבור נתוני קלט/פלט של מודל בין המארח להתקן). דוגמה להעתקה מוגזמת מודגמת בתצוגת המעקב למטה בזרמי GPU #167 , #168 ו- #169 .

image

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

3. גרעינים יעילים יותר במעבדי GPU

ברגע שהשימוש ב-GPU של התוכנית שלך מקובל, השלב הבא הוא לבחון את הגדלת היעילות של ליבות ה-GPU על ידי שימוש ב- Tensor Cores או fusing operations.

1. השתמש בליבות טנסור

למעבדי NVIDIA® GPU מודרניים יש ליבות Tensor מיוחדות שיכולות לשפר באופן משמעותי את הביצועים של גרעינים כשירים.

אתה יכול להשתמש בסטטיסטיקות ליבת ה-GPU של TensorBoard כדי להמחיש אילו ליבות GPU מתאימות ל-Tensor Core, ואילו ליבות משתמשות ב-Tensor Cores. הפעלת fp16 (ראה סעיף הפעלת דיוק מעורב להלן) היא אחת הדרכים לגרום לגרעיני התוכנית שלך להכפיל את המטריצה ​​הכללית (GEMM) (matmul ops) להשתמש בליבת Tensor. ליבות GPU משתמשות בליבות ה- Tensor ביעילות כאשר הדיוק הוא fp16 וממדי טנזור קלט/פלט מתחלקים ב-8 או 16 (עבור int8 ).

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

2. פיוז אופ

השתמש tf.function(jit_compile=True) כדי למזג פעולות קטנות יותר ליצירת גרעינים גדולים יותר המובילים לשיפורי ביצועים משמעותיים. למידע נוסף, עיין במדריך XLA .

3. אפשר דיוק מעורב ו-XLA

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

1. אפשר דיוק מעורב

מדריך הדיוק של TensorFlow Mixed מראה כיצד לאפשר דיוק fp16 במעבדי GPU. אפשר ל- AMP ב-NVIDIA® GPUs להשתמש בליבות Tensor ולממש עד פי 3 מהירות כללית בהשוואה לשימוש בדיוק fp32 (float32) בארכיטקטורות Volta וארכיטקטורות GPU חדשות יותר.

ודא שממדי מטריצה/טנזור עומדים בדרישות לקריאת ליבות המשתמשות ב- Tensor Cores. ליבות GPU משתמשות בליבות ה- Tensor ביעילות כאשר הדיוק הוא fp16 וממדי הקלט/פלט מתחלקים ב-8 או 16 (עבור int8).

שים לב שעם cuDNN v7.6.3 ואילך, ממדי קונבולוציה ירופדו אוטומטית במידת הצורך כדי למנף את ליבות Tensor.

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

1. השתמש בקרנל fp16 אופטימלי

כאשר fp16 מופעל, ליבות מכפלי המטריצה ​​(GEMM) של התוכנית שלך, צריכות להשתמש בגרסת fp16 המתאימה המשתמשת בליבות ה- Tensor. עם זאת, במקרים מסוימים, זה לא קורה ואתה לא חווה את המהירות הצפויה מהפעלת fp16 , מכיוון שהתוכנית שלך חוזרת ליישום הלא יעיל במקום זאת.

image

דף הנתונים הסטטיסטיים של ליבת ה-GPU מציג אילו פעולות מתאימות ל-Tensor Core ואילו ליבות משתמשות בפועל ב-Tensor Core היעילה. מדריך NVIDIA® לביצועי למידה עמוקה מכיל הצעות נוספות כיצד למנף את ליבות Tensor. בנוסף, היתרונות של שימוש fp16 יופיעו גם בקרנלים שהיו בעבר קשורים לזיכרון, שכן כעת האופציות ייקח חצי מהזמן.

2. קנה מידה אובדן דינמי לעומת סטטי

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

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

2. הפעל את XLA באמצעות tf.function(jit_compile=True) או אשכולות אוטומטיים

כשלב אחרון בהשגת הביצועים הטובים ביותר עם GPU יחיד, אתה יכול להתנסות בהפעלת XLA, שתמזג פעולות ויוביל לניצול טוב יותר של המכשיר ולטביעת זיכרון נמוכה יותר. לפרטים כיצד להפעיל את XLA בתוכנית שלך עם tf.function(jit_compile=True) או אשכולות אוטומטיים, עיין במדריך XLA .

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

2. מטב את הביצועים במארח יחיד מרובה-GPU

ה-API של tf.distribute.MirroredStrategy יכול לשמש כדי לשנות את הדרכת המודל מ-GPU אחד למספר GPUs על מארח יחיד. (למידע נוסף על אופן ביצוע הדרכה מבוזרת עם TensorFlow, עיין במדריכי ההדרכה המבוזרים עם TensorFlow , השתמש ב-GPU ושימוש ב-TPUs ובמדריך ההדרכה המבוזר עם Keras .)

למרות שהמעבר מ-GPU אחד למספר GPUs צריך להיות ניתן להרחבה מחוץ לקופסה, לפעמים אתה יכול להיתקל בבעיות ביצועים.

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

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

image

רשימת המשימות הבאה תעזור לך להשיג ביצועים טובים יותר בעת אופטימיזציה של הביצועים בתרחיש ריבוי GPU:

  1. נסה למקסם את גודל האצווה, מה שיוביל לניצול גבוה יותר של המכשיר ותפחית את עלויות התקשורת על פני מספר GPUs. שימוש בפרופיל הזיכרון עוזר לקבל תחושה עד כמה התוכנית שלך קרובה לשיא ניצול הזיכרון. שים לב שבעוד שגודל אצווה גבוה יותר יכול להשפיע על ההתכנסות, זה בדרך כלל עולה על יתרונות הביצועים.
  2. כאשר עוברים מ-GPU יחיד למספר GPUs, אותו מארח צריך כעת לעבד הרבה יותר נתוני קלט. לכן, לאחר (1), מומלץ לבדוק מחדש את ביצועי צינור הקלט ולוודא שזה לא צוואר בקבוק.
  3. בדוק את ציר הזמן של GPU בתצוגת המעקב של התוכנית שלך עבור כל קריאות AllReduce מיותרות, מכיוון שהתוצאה היא סנכרון בין כל המכשירים. בתצוגת המעקב המוצגת לעיל, ה-AllReduce מתבצע באמצעות ליבת NCCL , ויש רק קריאת NCCL אחת בכל GPU עבור ההדרגות בכל שלב.
  4. בדוק אם יש פעולות העתקה D2H, H2D ו-D2D מיותרות שניתן למזער.
  5. בדוק את זמן הצעד כדי לוודא שכל העתק עושה את אותה עבודה. לדוגמה, זה יכול לקרות ש-GPU אחד (בדרך כלל, GPU0 ) נרשם יתר על המידה מכיוון שהמארח בסופו של דבר משקיע עליו יותר עבודה בטעות.
  6. לבסוף, בדוק את שלב ההדרכה על פני כל ה-GPUs בתצוגת המעקב שלך עבור כל פעולות שמתבצעות ברצף. זה קורה בדרך כלל כאשר התוכנית שלך כוללת תלות בקרה מ-GPU אחד לאחר. בעבר, איתור באגים בביצועים במצב זה נפתר על בסיס כל מקרה לגופו. אם אתה מבחין בהתנהגות זו בתוכנית שלך, שלח בעיה של GitHub עם תמונות של תצוגת המעקב שלך.

1. בצע אופטימיזציה של שיפוע AllReduce

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

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

כל GPU משרשר תחילה את ההדרגות על פני שכבות הדגם, מתקשר אותן על פני מעבדי GPU באמצעות tf.distribute.CrossDeviceOps ( tf.distribute.NcclAllReduce הוא ברירת המחדל), ולאחר מכן מחזיר את ההדרגות לאחר הפחתת שכבה.

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

הזמן ל-AllReduce צריך להיות בערך כמו:

(number of parameters * 4bytes)/ (communication bandwidth)

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

שים לב שכל פרמטר של דגם הוא בגודל של 4 בתים מכיוון ש-TensorFlow משתמש fp32 (float32) כדי להעביר מעברי צבע. גם כאשר הפעלת fp16 , NCCL AllReduce משתמש בפרמטרים fp32 .

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

2. מחלוקת חוט מארח GPU

בעת הפעלת מספר GPUs, תפקידו של ה-CPU הוא להעסיק את כל המכשירים על ידי השקה יעילה של גרעיני GPU על פני המכשירים.

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

מציג המעקבים למטה מציג את התקורה כאשר ה-CPU מזיז את השקת ליבת GPU בצורה לא יעילה, מכיוון ש- GPU1 אינו פעיל ואז מתחיל להפעיל פעולות לאחר הפעלת GPU2 .

image

תצוגת המעקב עבור המארח מראה שהמארח משיק ליבות ב- GPU2 לפני שהוא משיק אותם ב- GPU1 (שים לב שהאופס של tf_Compute* להלן אינם מעידים על שרשורי CPU).

image

אם אתה חווה סוג זה של מסחרר של גרעיני GPU בתצוגת המעקב של התוכנית שלך, הפעולה המומלצת היא:

  • הגדר את משתנה הסביבה TensorFlow TF_GPU_THREAD_MODE ל- gpu_private . משתנה סביבה זה יגיד למארח לשמור על שרשורים עבור GPU פרטיים.
  • כברירת מחדל, TF_GPU_THREAD_MODE=gpu_private מגדיר את מספר השרשורים ל-2, וזה מספיק ברוב המקרים. עם זאת, ניתן לשנות את המספר הזה על ידי הגדרת משתנה הסביבה TensorFlow TF_GPU_THREAD_COUNT למספר השרשורים הרצוי.