הפוסט הזה בא בעיקר בעקבות הבאג האחרון בויסטה. העתקת כמות גדולה של קבצים (מעל 16,000) כאשר מותקן אנטי וירוס של קספרסקי גורמת לאכילת כל הזכרון הפנוי במערכת. הבאג קורה גם אם הקבצים מועתקים בשלבים (כלומר, לא כל ה-16,000 בבת אחת), וקורה בתנאים מסויימים גם בלי האנטי וירוס. הבאג קשור בצורה כלשהי ב-OLE ונובע מזליגת זכרון (סלאשדוט, הכתבה המקורית והתיקון ממיקרוסופט).
ולמה אני מעלה את הנקודה? לא כדי ללעוג לויסטה. באמת שלא חסרות סיבות לעשות את זה. הבעיה האמיתית פה היא מיקרוסופט. בפרט, זה לא העובדה שהיה באג. באגים קורים. כל ההתנהלות מסביב לוקה בחסר רב, שמיקרוסופט הבטיחה לנו שמאחורינו.
נתחיל בהתחלה. אחת הבעיות החמורות ביותר במערכות ההפעלה של מיקרוסופט היא כמות הקוד החדש שנכתב עבור כל מערכת מחדש. מיקרוסופט כמובן משוויצים בכמות הקוד החדשה שאתם משלמים עליה בכל פעם שאתם משדרגים, אבל התכלס הוא שהם לוקחים קוד שעבר את בקרת האיכות היחידה ששווה משהו, כלומר שימוש ע”י אנשים, וזורקים אותו לפח. במצב כזה, נקל לראות מדוע מקובל לחשוב שרק אחרי service pack 2 יש טעם להתחיל להשתמש במוצר של מיקרוסופט.
החלק השני של הסיפור הוא חוסר ההתאמה בין מה שמיקרוסופט ממליצים למפתחים לעשות, לבין מה שהם עושים בעצמם. מיקרוסופט מנסים לשכנע את כל העולם לעבור ל-.Net מעצם העובדה שהבאג שהתגלה, כלומר זליגת זכרון, אינו יכול לקרות תחת .Net, נקל לראות שהם לא עומדים בעיצה שהם מייעצים לאחרים בעצמם.
אז מה היה לנו בנתיים. היה לנו קוד חדש על פונקציונליות ישנה (העתקת קבצים), שנכתב בטכנולוגיה שמיקרוסופט עצמם מגדירים כמיושנת. מספיק חמור, לא?
אולי.
כי יש פה גם בעיה של בדיקות. כיום קיימים כלים מאוד מפותחים שיודעים להתריע על הסוג הזה של הבעיה, מבלי לחכות שגודל הזליגה יגרום לכל המערכת לקרוס. אני יכול לסלוח לבודק האיכות שלא חשב לבדוק העתקה של מעל 16 אלף קבצים בעלי stream חליפי. קשה לי יותר לסלוח לבודק שלא חשב לבדוק העתקה של קבצים עם stream חליפי באמצעות כלי שמתריע על זליגות זכרון. אלא אם יש כל כך הרבה מהם שהזליגה הזו, עד שהיא לא מגיעה למימדים גדולים, נבלעת ברעש, מדובר בכשל חמור בתהליך הבדיקה.
כשויסטה רק יצא, אני (כמו הרבה אחרים) התלוננתי שמדובר במערכת שלקח לה יותר מידי זמן לצאת, ובסופו של דבר גם לא מכילה מספיק חדשנות כדי להצדיק מעבר. היום אנחנו נאלצים להוסיף לרשימה גם “ויצאה מוקדם מידי”.
בהחלט ייתכן שהכוח של מיקרוסופט יהיה מספיק גדול כדי לגרום למחשבים חדשים לצאת רק עם ויסטה, ובכך לגרום למערכת הזו להפוך להיות המערכת הדומינטית בעוד כמה שנים. מה שכן, אני, באופן אישי, לא רואה איך זה קורה אם מיקרוסופט נותנת לשוק לבחור בין מערכות מיקרוסופט השונות באופן חופשי.
שחר
אני לא רגיל להיות בעמדה של פרקליט השטן, אבל:
1. אי אפשר לכתוב הכל מעל דוט נט בקוד מנוהל, וגם אם כן – OLE היא טכנולוגיה ותיקה (הרבה לפני דוט נט) ומאוד תשתיתית. מצד אחד אתה טוען שהם כותבים יותר מדי קוד חדש ומצד שני שואל למה הם לא שכתבו את OLE לדוט נט (ובעקבות כך כמויות קוד אדירות נוספות שמשתמשות בOLE, רובן בכלל לא של מייקרוסופט).
2. מה שאתה אומר שכל זליגת זיכרון היא “כשל חמור בתהליך הבדיקה”. אני לא מסכים, הרבה מאוד תוכנות זולגות, ומשתמשים עדיין משתמשים בהם בששון, ואפילו לא יודעים על הבעיה. יש בעיות יותר חמורות מזליגת זכרון, ואם אלו עוברות את תהליך הQA, מה יגידו איזובי הקיר?
מה גם שבדיקה עם כלי לבדיקת זליגת זכרון זה לא תהליך טיפוסי שמהנדס QA יכול לעשות, פשוט כי אין לו את הרקע הטכני. זה תהליך שמפתחים אמורים לבצע – ושנינו יודעים שזה אחד הדברים שמפתחים כמעט לא עושים.
1. אני טוען שלא היה צריך לשכתב. תסכים איתי שאם לא היו משכתבים, לא היה צץ הבאג הזה, נכון? כמו כן, אני טוען לצביעות מצד מיקרוסופט, שמצד אחד דוחפת את כל העולם לכיוון של נט, ומנגד לא משתמשת בטכנולוגיה הזו בעצמה. אני לא מכיר את ויסטה, אבל אני כן יכול להגיד לך שבין XP ו-XP SP2 כמות השימוש בנט בתוך מערכת ההפעלה ירד
2. כל באג שניתן היה לגלות בתהליך בדיקה אוטומטי הוא כשל אם הוא מגיע למוצר הסופי. זה נכון שבעתיים למערכת שעל פי הטענות הושקע בה כל כך הרבה בבדיקה. איש בדיקה לא אמור לדעת לפענח את התוצאות של כלי הבדיקה האוטומטיים. הוא רק אמור לדעת שלא אמורות להיות מדווחות בעיות, ובמידה שמדווחות, להעביר את הדו”ח למפתח שאמור לפתור את הבעיה. אני ממש לא מקבל את הטיעון של “איש הבדיקה לא אמור לדעת”.
כמובן שיש פה בעיה בסיסית יותר, וזה שהתוכנות שמריצות את מערכת ההפעלה של חלונות לא עוברות בדיקה אוטומטית בלי שגיאות. אני זוכר שכאשר נתקלתי לראשונה ב-bounds checker, הוא תמיד דיווח על שגיאות בתוך הקוד שהוכנס לתוכנה שלי ע”י visual studio. ההסברים של NuMega היו שמדובר בבאגים אמיתיים, גם אם לא בקוד שלי.
הבעיה היא שברגע שאתה מתרגל שהכלים האלו מדווחים על שגיאות שאתה צריך להתעלם מהן, כל התהליך הופך להיות פחות אפקטיבי.
שחר
מעבר למה שעמרי אומר, אם ניקח את קריטריון זליגת זיכרון כקריטריון חשוב שבגללו אנחנו מחליטים באיזו תוכנה להשתמש, אז כל מי שמשתמש בשועל על חלונות צריך דחוף להחליף דפדפן.
בכל מקרה הגענו לימים טובים בהם כבר לא טוענים שחלונות מתרסקת כל הזמן, כי אין הרבה אנשים שמעתיקים 16 אלף קבצים בפחות מחודש.
טוב עכשיו גם טרחתי לקרוא את תיאור הבאג. נו, הלוואי על כל תוכנה שיהיו בה רק באגים כאלו, שחורגים הרבה מדפוסי השימוש הרגיל, כאשר היא יוצאת לשוק.
גם אני לא אוהב להיות בצד הMSי (אני מעדיף לחשוב שאני אובייקטיבי =)) וחייב להסכים שקוד ברמת הOS לא נשמע סביר שיכתב בדוט-נט.
כמו כן, ברור שאפשר לעשות דליפות זכרון גם בקוד דוט-נד (נגיד, שכחת לרוקן את רשימת הinodeים או מה שלא יהיה שהשתמשת בהם ולכן הgarbage collector לא יכל למחוק את המידע).
אני כן מסכים איתך שהשכתוב המסיבי הוא מיותר ובטח נובע מחפיפה לא טובה של קוד או חוסר רצון לחפוף קוד וההעדפה הברורה של הרבה מתכנתים לכתוב בעצמם מחדש.
אביב.
מסתבר שהפוסט הזה שם אותי במוד של לא להסכים עם קוראי (או, לפחות, עם מגיבי).
מה שאני אומר זה לא שזליגת זכרון זה באג יותר חשוב מאשר באגים אחרים. מה שאני אומר זה שגליגת זכרון זה באג שיש כלים אוטומטיים שמגלים אותו, ועל כן אין תירוץ לתוכנה מחברה שמסוגלת להרשות לעצמה לקנות את הכלים האלו לצאת עם הבעיה הזו, לפחות לא על פעולה כל כך יומיומית כמו העתקת קבצים.
במילים אחרות, זה לא שהבאג חמור, כמו שהיה אמור להיות קל למצוא אותו.
לגבי חריגה מדפוסי השימוש הרגיל, קבצים עם alternate streams לא אמורים להיות כל כך נדירים. כל קובץ בר הרצה ש-IE מוריד מהאינטרנט הוא מוסיף לו alternate stream. בדיקה של העתקת קבצים שמכילים alternate stream אמורה היתה להתבצע.
זה נכון שלא רואים את הבעיה בעין אלא אם מעתיקים הרבה קבצים, אבל, כמו שאמרתי, יש כלים אוטומטיים שאמורים היו להתריע על הבעיה עוד לפני שרואים אותה בעין.
שחר
חברה, אני מספיק בוגר כדי לא להסכים עם אנשים בלי להאשים אותם שהם תמיד בצד של מיקרוסופט. למעשה, היו אפילו מספר פעמים שאני הייתי בצד של מיקרוסופט.
לגבי נט – אני חושב שיש חוסר הבנה מאוד רחב של איך בנוויה חלונות. מתוך היכרות עם הנושא בגלל Wine אני יכול להגיד לכם שמעל 90% ממערכת חלונות כתובה בממשק של חלונות, כלומר ב-Win32. בעוד שהקרנל בחלונות אכן מכיל הרבה יותר ממה שהיה בריא שהוא יכיל, הוא עדיין לא מכיל הרבה ביחס לגודל הכולל של המערכת.
עכשיו, מיקרוסופט מנסה לטעון שדוט-נט מתאים להכל, שיש לו ביצועים שמשתווים לקוד native וכו’. כשאתה אומר שנראה לך לא סביר לכתוב מערכת הפעלה בדוט נט, אתה בעצם אומר שאתה לא מאמין להם. אני אומר שזה שהם מקטינים, ולא מגדילים, את כמות השימוש שלהם בדוט נט אומר שגם הם לא מאמינים לעצמם, ועל כך הביקורת שלי.
לגבי ה-garbage collector – עברתי באיזה קורס (כבר לא זוכר איזה אחד – אולי תכנות מקבילי ומבוזר) הסבר על אוסף האשפה של ג’אווה, וההסבר כלל איך הוא יודע שאפשר לשחרר רשימה שמצביעה לעצמה (כך שה-reference count שלה הוא לא אפס), אבל שאין אליה שום קישור אחר. אם ג’אווה יודע לשחרר את הזכרון במצב כזה, אני מניח שגם דוט-נט.
אני לא בטוח שהשכתוב המסיבי נובע מתרבות הנדסה קלוקלת בתוך מיקרוסופט. אני נוטה יותר לחשוב שהחברה מנסה להרחיב בכל גרסה את מגוון השירותים שהמערכת נותנת, מתוך מטרה שתוכנות חדשות יסתמכו על השירותים החדשים, ועל כן לא ירוצו על מערכות ישנות יותר. הגישה הזו, לדעתי, היא שגורמת לכך שכמעט אין מערכת בתוך חלונות שלא עוברת שינוי מסיבי בין גרסאות.
שחר
נ.ב.
חוק שנייר היה דורש ממני שאני אגיד שהסיבה שהם מרחיבים את השירותים היא שהם באמת מאמינים שיותר שירותים = יותר תרומה למפתח.
1. יש לך טעות בפוסט, רשמת “שלח” במקום “שלקח”.
2. מיקרוסופט ניסו לבסס את ויסטה עם CODEBASE משמעותי ב .NET אבל הם זרקו את הקוד הזה בערך ב 2004 או 2005 ושכתבו אותו מחדש ב WIN32 הרגיל.
3. גם ל SUN היתה את הבעיה הזו עם JAVA ובשלב מסוים היא המליצה במייל פנימי לא להשתמש ב JAVA בפרויקטים בגלל בעיות ביצועים.
4. ב .NET יש דברים שמתממשקים עם NATIVE OS ולדברים האלו אתה אחראי לשחרר בעצמך (למשל אובייקטים גרפים של הממשקים של מערכת ההפעלה), אחרת הם פשוט יזלגו לך בתוכנית .NET שלך (יותר נכון במערכת ההפעלה, אבל עדיין הקוד שאתה כותב הוא 100% .NET).
5. יש דברים שמה לעשות, לא ישתוו לביצועים של שפות דור קודם כמו C++ וכו’.
6. מיקורוסופט עובדת על מערכת הפעלה מחקרית שכתובה כמעט 100% בשפה מונהלת מבוססת C#, כאשר רק התקשורת הבסיסית ביותר עם החומרה היא ב ASM:
http://research.microsoft.com/os/singularity/
יש שם כמה מסקנות די מעניינות מהמחקר ביצועים / סיבוכיות קוד (בגלל ההנחה שהכל רץ בסביבה מאובטחת).
איפה התגובה הדי-גדולה שרשמתי פה?
תגובות ממגיבים חדשים מחכות שאני אאשר אותן. אחרת הבלוג הזה היה מקבל כמאתיים תגובות ספאם חדשות מידי יום.
שחר
וואו, לא כולם לא מסכימים איתי 🙂
1. תודה, תוקן.
2. כן, זה מה שאמרתי 🙂
3. אני לא זוכר שסאן ניסו לטעון שכל העולם ואישתו צריכים לעבוד רק בג’אווה, אבל גם אם כן, מעולם לא טענתי (ואני לא מסוגל לטעון) שסאן לא משמיעים טענות מצחיקות גם כן.
4. על פי מה שניתן היה להבין, היית אמור להיות מסוגל להסתדר בלעדיהם. אין לי הרבה נסיון בסביבות של Managed code, אבל אני יכול להגיד לך שאם אני עובד מול מערכת הפעלה מתוך C++, אני מאוד משתדל שכל המשאבים שאני מקצה ממערכת ההפעלה יהיו מנוהלים. כמובן שעצם העובדה ש-Managed code לא מאמין ב-destructors מקשה על לייצר מחלקה שדואגת להחזיר את ה-file handle באופן אוטומטי כשהיא מסיימת איתו. הדיון הזה, כמובן, כבר גולש לדיון על האם הצורה שבה ג’אווה/דוט נט בנויות חכמה או לא.
5. הטענה היתה שכן, יש דברים שלא ישתוו, אבל הם מעטים ולא מתייחסים לתוכנות גדולות (למעט קרנל). אפילו ראיתי טוענים שיש מקרים שבהם ג’אווה מהירה מאשר C/C++. אני מסכים אם מה שאתה אומר, אבל רציתי רק להפנות תשומת לב לכך שיש אנשים שלא מסכימים.
6. לא יצא לי עדיין להתעמק, אבל זה נשמע לי קצת כמו הרעיון של mini kernel. נפלא בתיאוריה, אבל לא עובר את מבחן המציאות.
שחר
3. http://www.archub.org/javamemo.txt (לא טענתי שטענת).
4. בשביל זה המציאו את המנשק/מתודה IDisposable Dispose
http://msdn2.microsoft.com/en-us/library/system.idisposable.aspx
כל מחלקה שמחזיקה בדברים שהם לא MANAGED מומלץ שתגדיר את המנשק. מי שמתמש במחלקה באחריותו לדאוג לקרוא לזה (בלי קשר ל destructor) והשפה מספקת דברים כמו using בסינטקס בשביל לעזור.
5. שפות שהם BYTECODE ויש להם קומפיילר מסוג JIT או Just In Time כשהפופלריות הם כמובן אלו מסוג .NET ו JAVA יכולים לעיתים להשיג ביצועים עדיפים על C/C++ כי תוכנית C בד”כ מקומפלת למכנה משותף נמוך ביותר, או לפלטפורמה ספיציפית, ככה שהאופטימזציות מבחינת ההרצה מוגבלות למה שהמחבר עשה. ב JIT הקוד נמצא בשלב ביניים ומורץ בקוד מכונה רק בזמן ריצה ולכן ה JIT יכול לבצע אופטימיזציות ספיציפיות למערכת עליה רצים. (גם אופטימיזציות מעבד למשל תמיכה ב SSE וכו’, וגם אופטימזציות קוד כמו שיטות חדשות להסתכלות על פעולות מסוימות, הסתכלות קדימה וכו’).
6. אכן מדובר במשהו אקדמי, אבל מהסתכלות על המסמך תזה שיצא מזה לפני כמה ימים/שבועות התפלאתי לראות כמה מסקנות די נחמדות שם (עדיין לא התעמקתי בו).
שיטת המיספור נהיית קצת מגוחכת, אבל נמשיך איתה 🙂
4. זה לחלוטין לחלוטין לא מספק. כל הכוח של ה-Destructors של C++ הוא שאתה יכול לזרוק Exception ולדעת שכל מה שהקצאת השתחרר, בלי סימני שאלה, תהיות ושאר דברים שגורמים לנזק. מרגע שאתה צריך לקרוא ל-Dispose ישירות, כל הכוח ש-destructors נתנו לך הלך לאיבוד.
5. כן, זה חלק מהטיעונים ששמעתי. אני בהחלט לא חושב שאני קונה אותם. בוודאי לא את החלק של אופטימיזציות SSE. לבצע אופטימיזציות דורש דברים כמו loop unrolling וניתוח תלויות בין איטרציות של הלולאות. זה משהו שקומפיילר, שמקבל תמונה די מלאה על המבנה הכללי של התוכנה, יכול לעשות אם הוא מתאמץ. אני לא רואה איך JIT מסוגל לקבל את התמונה המלאה הדרושה, או לעשות את זה בלי לגרום להאטה נטו של התוכנה (כולמר, שהפעולה של האופטימיזיר לא לוקחת יותר כח חישוב מאשר מה שהתוצר שלו חוסך). אלו תיאוריות יפות, אבל אני לא כל כך רואה אותן מתרגמות לשפת המעשה.
וזה לא הכל – המעבדים עצמם נושפים בעורפיהם. מעבד מודרני של אינטל עושה פעולה לא כל כך זניחה של להפוך את קוד שפת המכונה לקוד (הייתי קורא לזה מיקרו קוד, אבל מדובר במעבדי Risc שאין להם מיקרו קוד במובן הרגיל של המילה) פנימי שירוץ יותר מהר, כולל ביצוע מקבילי. עד כדי כך התופעה מגיעה שיש הטוענים שהאופטימיזציה הכי טובה מבחינת מהירות לתוכנה כיום היא אופטימיזצית גודל. ככל שהתוכנה יותר קטנה, יותר ממנה נכנס במטמון של המעבד, והיא רצה יותר מהר. מאוד קשה לי לראות איך JIT מייצר, כאשר יש לו מגבלות כוח חישוב על הקומפיילר, קוד יותר קטן מאשר קומפיילר מלא.
וזה לפני שהעליתי את הנקודה ש-Bytecode הוא לא נראה שונה מכל שפת מכונה CISC אחרת. במילים אחרות, לא נראה מההגדרות של לפחות Java שהוא אמור להיות קל במיוחד לפירסוס, או מכיל מידע על זרימת המידע בתוכנה, יותר מאשר שפת מכונה רגילה.
6. גם mini kernel הפיק מסקנות מעניינות. זה לא אומר שאתה רוצה להריץ מערכת הפעלה שכתובה מעליו. אני לא נגד מחקר אקדמאי, כל עוד לוקחים את תוצאות המחקר בהקשר הראוי להן.
שחר
4. בשביל זה יש finally block שהוא חלק (אופציונאלי) מה try…catch statement שאתה יכול להיות בטוח שהוא יתבצע (כמעט) תמיד, חוץ ממקרים מאד מסויימים, של סוגי Exception מאד מסויימים (נראה לי 3 מקרים), שאם הם קורים הדוט.נט מפרק לחלוטין את החבילה, ומעיף את האפליקציה שלך לחלוטין.
שחר.
אבל אני צריך לכתוב אותו בצורה מפורשת. אני צריך לתאם בין שני חלקים שונים של אותה פונקציה כדי לוודא שכל מה שהקצאתי וצריך קריאה ל-Dispose באמת מופיע בה. אני צריך להבין האם באמת ההקצאה שעשיתי פה סיימה את חייה פה, או שעברה ל-context אחר, ואז אני צריך, בדרך פלא כלשהי, לספר להקשר האחר שהוא צריך חלק finally בשביל מה שאני הקצאתי.
ואני צריך להגדיר אותו בכל פונקציה ופונקציה, בגלל שיכול להיות שפונקציה שאני קורא לה מהפונקציה שלי תזרוק exception שפונקציה שקרה לי תתפוס.
במילים אחרות, כאב הראש שאתה מציע פה הוא בלתי נסבל לחלוטין. תשווה את זה לפשטות של C++, שבה אתה כותב “throw”, וכל מה שהקצאת שוחרר.
שחר
במקרים מסויימים, אתה יכול לעקטוף הכל ב using statement של האובייקט העיקרי שבו אתה משתמש. נניח, אני משתמש באובייקט לקריאת קובץ, ואני רוצה שמיד בסיום הפעולה, בין אם באופן טבעי ובין אם בבת אחת הכל ישתחרר, אני שם את האובייקט ב using ואז אני יכול להיות בטוח בנושא זה.
בכל אופן כשזורקים exception לא הכל משתחרר בעיקר בשביל לאפשר לך את המשך הפעולה ולהחליט בעצמך מה אתה רוצה לעשות.
שחר.