שיעור 22: תנאי מרוץ בזיכרון גלובלי — למה צריך atomics
עד עכשיו כמעט כל kernel שכתבתם נתן לכל thread תא פלט משלו: c[i] = a[i] + b[i], כאשר thread מספר i הוא היחיד שנוגע אי-פעם ב-c[i]. מכיוון ששני threads לעולם לא חולקים יעד, הסדר שבו הם רצים לא משנה — התוצאה תמיד נכונה. אבל יש בעיות שמכריחות הרבה threads לעדכן את אותו תא בדיוק: לספור כמה ערכים נופלים לכ
דמיינו 30 ילדים שחולקים דף ספירה אחד שכתוב עליו 5. שניים מציצים בו יחד, שניהם רואים 5, שניהם מוחקים וכותבים 6. שני ילדים ספרו, אבל בדף כתוב 6 ולא 7 — ספירה אחת נעלמה. פעולה אטומית היא כמו חוק שרק ילד אחד מחזיק את העיפרון בכל רגע: קורא, מוסיף, כותב, ומעביר הלאה. ככה שום ספירה לא הולכת לאיבוד.
- תנאי מרוץ (race condition)
- באג שבו התוצאה תלויה בתזמון ובסדר הבלתי-צפויים שבהם threads ניגשים לאותו נתון. לפעמים עובד, לפעמים לא.
- קריאה-שינוי-כתיבה (read-modify-write)
- הפעולה x++ היא בעצם שלושה צעדים: לקרוא את x, להוסיף 1, ולכתוב את x. thread אחר יכול להידחק בין הצעדים.
- עדכון אבוד (lost update)
- שני threads קוראים את אותו ערך ישן ושניהם כותבים בחזרה, כך שאחד ההגדלות פשוט נעלם והספירה יוצאת נמוכה מדי.
- פעולה אטומית (atomic operation)
- פעולת read-modify-write בלתי-ניתנת-לחלוקה שאף thread אחר אינו יכול לקטוע — הפתרון למרוץ, למשל atomicAdd.