אובייקטים בזיכרון
אובייקטים פשוטים ופרימיטיביים יכולים להיות ממופים לזכרון והפוך.
זה אומר שניתן לקחת זיכרון ופשוט להמיר באותו לאובייקט:
1 | struct A |
גודל משתנים
אי אפשר לדבר על זיכרון ללא אזכור לנושא גודל המשתנים.
זה די נפוץ להניח ש-int
הוא 32 סיביות בעוד ש-long
הוא 64 סיביות.
רובנו מניחים שגודל המשתנים הוא כך:
Short
הוא 16 סיביות.Int
הוא 32 סיביות.Long
ו-Ptr
הם 64 סיביות.
הסטנדרט Data Model
הוא קובע מהו גודל המשתנים.
אנחנו נניח שאנחנו משתמשים ב-LP4
.
ע”י הרצה מאוד פשוטה של sizeof(type)
דרך קוד נוכל לבדוק את גודל המשתנים.
אצלי:
1 | Short 2 |
יישור של אובייקטים
אובייקט מגדיר את ערך ההתיישרות שלו שהוא מסוג size_t
.
ההתיישרות מתארת כיצד לסדר את האובייקט בזיכרון על מנת שאובייקטים סדרתיים יוכלו להיות במקשה אחת.
נתון לנו את המבנה הבא:
1 | struct A |
ל-A
יש 2 מספרים אשר כל אחד מהם הוא 4 בתים.
לכן היישור של האובייקט הוא 4 בתים.
בעזרת האופרטור alignof
נוכל לבדוק זאת בקלות:
1 | int main() |
יישור האובייקט A
בנוי בצורה מושלמת פה כי ערך היישור הוא 4 בתים.
האובייקט בנוי מ-2 משתנים מסוג int
אשר תופסים סך הכל 8 בתים.
מה ייקרה אם נוסיף משתנה נוסף מסוג char
?
1 | struct A |
כדי לענות על השאלה הזו נוכל לבדוק בעזרת alignof()
ו-sizeof()
:
לפני:
1 | Alingment of Previous A is 4 |
אחרי:
1 | Alingment of A is 4 |
מה קרה פה?
אנחנו יודעים ש-char
הוא בגודל בית בודד.
איך הגענו מ-8 בתים ל-12 בתים?
זה הכל בגלל תהליך היישור!
הקומפיילר יודע לבצע יישור אוטומטי מבלי לומר לערך המינימלי ביותר שהוא יכול ליישר מבנה לפיו.
יישור קיים על מנת שנוכל למפות אובייקטים בצורה מיטבית בזיכרון:
מה ייקרה אם ננסה להתעלם מיישור?
השורה ראשונה והשנייה כבר לא מיושרות!
למה מיישרים לחזקת 2?
- ככה המחשב בנוי
כל ביט במחשב הוא ערך בינארי - 0 או 1.
סוגי משתנים הם גם כפולות של שתיים.
Byte
- 8 בתים,Short
- 16 בתים,Int
- 32 בתים,Long long
- 64 בתים.
על מנת שלא נצטרך לחשב רווחים בין אובייקטים לא מיושרים העדיפות היא להשאיר אותם בחזקת 2 או יישור ע”פ גודל הבתים.
למשל בגראפיקה תלת מימדית התמונות בכרטיס מסך שמורות בחזקת 2.
למשל בייצור Mipmaps
.
- Cache lines ו-Pages.
כדי לבצע אופטימיזציות ביצועים זיכרון המטמון בנוי בצורה כזו שאנחנו צריכים לטעון חזקות 2 של זיכרון.
גודל נפוץ הוא 64 בתים לשורה בזיכרון מטמון.
כך שאם יהיה לנו אובייקט בגודל 68 בתים - נצטרך לטעון 2 שורות במקום 1.
ניקח את המבנה האחרון כדוגמא - אם נעבור על רשימת אובייקטים בלולאה, איך הזיכרון ייצטרך להיטען?
1 | for(auto& AObj : listOfAs) |
ריווח
רווחים באובייקטים קיימים כדי להשלים יישור.
דוגמא א
1 | struct A |
מבנה A
בגודל 12
בתים והוא מיושר ב-4
.
דוגמא ב
1 | struct A |
מה ייקרה כאשר נזיז את char Value3
לשורה העליונה?
האם זה משפיע על המבנה?
כן ולא.
הריווח קיים בתוך המבנה ולא בסופו אבל הוא עדיין קיים.
דוגמא ג
1 | struct A |
גודל המבנה כעת הוא 24
בתים!
איך הוא בנוי?
Value
הוא בגודל 4 בתים.Value2
בגודל בית ויש לו עוד 3 בתים לריווח.Value3
הוא בגודל 8 בתים, Value
ו-Value2
שניהם מיושרים ל-8.Value4
הוא 4 בתים מכיוון שהיישור ל-8 אנחנו מוסיפים 4 בתים לריווח.
1 | int 4 bytes |
4 + 1 + 3 + 8 + 4 + 4 = 24
מהו המינימום ליישור כעת?
היישור הוא ע”פ המשתנה long long
- 8 בתים.
alingas
על מנת לבקש ריווח שונה נוכל להשתמש ב-alignas
.
שימו לב שאי אפשר לבקש יישור שונה מהמינימום שהקומפיילר מזהה.
1 | struct alignas(16) A |
מבנה A
מיושר ל-16 וגודלו הכולל הוא 32
.
למה אנחנו צריכים יישור שונה?
הסיבה הבסיסית ביותר היא - ביצועים.
כאשר המעבד טוען זיכרון הוא רוצה לטעון את הזיכרון הנחוץ ביותר.
כאשר הוא טוען זיכרון לא נחוץ - טעינה נוספת תצטרך לקרות ואת זה אנחנו מגדירים כ”החטאה בזיכרון”.
יותר החטאות = ביצועים פחות טובים.
ניתן לראות זאת בדוגמא הקודמת:
אם שורה בזיכרון המטמון שלנו הוא 24 בתים - כדי להביא את האובייקט השלישי נצטרך לבצע טעינה של 2 שורות.
על מנת שנוכל ליישר ל24 נצטרך 2 אובייקטים - כל גודל אובייקט הוא 18 בתים ויש לנו ריווח כולל של 6 בתים.
אילו בעיות עולות מחורים בזיכרון?
- בעיות פרגמנטציה שיביאו לשימוש גדול יותר של זיכרון.
עבור 2 אובייקטים נאבד מהשימוש 6 בתים.
ב-100 אובייקטים נאבד 600 בתים!
בקבצים גדולים נוכל להחזיק לעיתים גם עשרות אלפי אובייקטים.
ב10,000 אובייקטים נאבד 58 קילובייטים!
זה מלא.
זוהי הסיבה למה קיים ריווח באובייקטים.
תוודאו שהמשתנים מסודרים כמו שצריך ויהיו לכם פחות בעיות עם ריווחים!
לא מסודר:
1 | struct A |
1 | Alingment of A is 4 |
מסודר:
1 | struct A |
1 | Alingment of A is 4 |
כאשר יוצרים אובייקטים בסיסים ומבנים תוודאו שאתם לא פוגעים ביישור.
חוק בסיסי ללכת על פיו - תסדרו את האובייקטים מהגדול לקטן.
תודה על הקריאה!