מטמון דפים וייעוץ למדיניות זיכרון¶
נניח שסורק קבצים קורא ארכיון גדול פעם אחת, ובמקביל שירות אחר צריך את אותם עמודי זיכרון לבקשות קצרות. הקורא הראשון אינו רק צורך רוחב פס מהדיסק. הוא משפיע גם על המטמון ועל עבודת reclaim של המערכת. כדי לבחור מדיניות שימוש, צריך להבין איזה נתון ניתן לזרוק, איזה נתון מלוכלך ואיזה נתון יידרש שוב בקרוב.
ה־page cache מחזיק דפים של קבצים ומאפשר לקריאות חוזרות להשתמש בנתונים שכבר נמצאים בזיכרון. כתיבה יכולה לשנות עמודים במטמון ולסמן אותם dirty לפני שהאחסון קיבל אותם. התהליך יכול לסיים את קריאת write בזמן שעבודת writeback תתרחש מאוחר יותר. לכן זמן כתיבה קצר אינו תמיד מדד לזמן שבו המידע נעשה עמיד.
כאשר הזיכרון נדרש למטרה אחרת, הקרנל בוחר אילו עמודים לפנות או לכתוב. ההחלטה מושפעת מדפוסי גישה, מצב dirty ומדיניות המערכת. האפליקציה יכולה לתת רמזים כגון גישה סדרתית או שימוש חד־פעמי, אבל הרמז אינו חוזה תזמון שמחייב תוצאה אחת בכל עומס.
נראה פעולה עם סמנטיקה חזקה יותר עבור מיפוי אנונימי פרטי. הוסיפו ל־Mapping מההרצאה השנייה את המתודה הבאה. היא דורשת השאלה משתנה משום שהנתונים עשויים להשתנות בעקבות הפעולה.
impl Mapping {
fn discard_contents(&mut self) -> io::Result<()> {
let result = unsafe { libc::madvise(self.pointer.as_ptr().cast(),
self.len, libc::MADV_DONTNEED) };
if result == -1 { Err(io::Error::last_os_error()) } else { Ok(()) }
}
}
fn main() -> io::Result<()> {
let mut memory = Mapping::new(4096)?;
memory.bytes_mut().fill(7);
assert_eq!(memory.bytes()[0], 7);
memory.discard_contents()?;
assert_eq!(memory.bytes()[0], 0);
Ok(())
}
הקטע מחליף את main הקודם ומצטרף לאותו טיפוס בעלים. עבור סוג המיפוי המסוים הזה בלינוקס, שימוש מחדש אחרי MADV_DONTNEED מקבל תוכן אפס בהתאם לחוזה. אין להעתיק את הציפייה הזאת לכל מיפוי משותף, קובץ או התקן. משמעות העצה תלויה במקור המיפוי ובמצבו.
המתודה אינה מקצרת את מרחב הכתובות. הכתובת והאורך של האובייקט נשארים, אבל המערכת יכולה לשחרר משאבים פיזיים ולהחזירם בנגיעה הבאה. לכן אחרי הפעולה נוכל לראות ירידה במצב התושב ואז faults חדשים במעבר הבא. שוב, המפה הווירטואלית והזיכרון התושב הם שני דברים שונים.
הפעולה MADV_FREE מדגימה מדיניות אחרת: היא יכולה לסמן עמודים כניתנים לשחרור עצל, כך שהנתונים הישנים עשויים להישאר זמינים עד שהמערכת תשתמש באפשרות לפנות אותם. קוד שנשען על כך שהבייטים עדיין שם מפר את מטרת החוזה. אם האפליקציה זקוקה לתוכן, היא אינה רשאית לומר למערכת שאפשר לוותר עליו.
רמזי גישה כמו MADV_SEQUENTIAL ו־MADV_RANDOM מתארים דפוס צפוי. עבור קבצים אפשר לחקור גם posix_fadvise, שמדווח שגיאות לפי החוזה שלו ולא בהכרח דרך errno כמו syscall רגיל. נמדוד את ההשפעה על זמן, faults וקריאות אחסון, ונרשום אם ההרצה הייתה על cache חם או קר.
מדידה של cache קר אינה פשוטה כמו קריאה שנייה באותה לולאה. פעולות קודמות של התהליך או של תוכנית אחרת יכולות להכין את הנתונים. במעבדה נשתמש בקובץ ייעודי ובסביבה מבודדת כשצריך לשלוט במצב המטמון. אין צורך לשנות את המטמון הגלובלי של מחשב העבודה כדי לבדוק כל אופטימיזציה קטנה.
גם prefetch אינו בקשה שמבטיחה שהנתון יגיע בדיוק בזמן. הוא יכול להקדים עבודה מועילה, אבל יכול גם להביא נתונים שלא נשתמש בהם ולהגדיל תחרות על רוחב פס. לפני הוספתו נזהה מסלול גישה יציב ונבדוק אם ההמתנה באמת נובעת מהבאת נתונים ולא מעבודה אחרת.
במערכת שמייצרת הרבה dirty pages, האטה יכולה להופיע בזמן כתיבה מאוחרת אף שהכתיבות הראשונות היו מהירות. זאת דרך של המערכת להגביל פער בין ייצור נתונים לבין קצב writeback. מדידה של כמה מילישניות בתחילת העומס עלולה להחמיץ את המצב היציב לחלוטין.
החוזים של העצות מופיעים בעמוד madvise. הבחינו בין פעולה שמוותרת במפורש על תוכן לבין רמז מדיניות, והוכיחו בממשק הראסט שאין הפניה פעילה שמבטיחה תוכן בזמן שאנחנו מבטלים אותו.