May 16, 2008

Profiler, GC וילקוט הכזבים

בלי הקדמות מיותרות:
  • ה- Garbage Collector של פלאש עובד רע, נורא, גרוע... אם בכלל.
  • הפרופיילר המוצע בפלקס 3 יכול לסמא את עיניו של מומחה ה- performance הגדול ביותר.
  • בדיקות performace אמורות להיעשות רק על נגן רגיל, ולא נגן debug.

בואו נתחיל מהבסיסי.
יש לא הרבה מאמרים על "איך ה GC של פלאש עובד?", והתיעוד של אדובה לא ממש משפר את המצב. מה שכן, אני יכול להעיד (בתור אחד שקרא את רובם בשבועות האחרונים) על דבר אחד: כולם, ללא יוצא מן הכלל, לאחר כל השורות והמילים המפוצצות, אומרים דבר אחד: הוא לא עובד טוב. לא, תנו לנסח מחדש: הוא עובד רע.
מה זה "לא עובד טוב"? למעשה זה אומר שאי אפשר לצפות מתי הוא ירוץ, ומאידך כאשר הוא רץ אי אפשר לבטוח בו שהוא ינקה את כל מה שסומן לניקוי. שיטות סימון האובייקטים לאיסוף הן די סטאנדרטיות לסביבות ריצה (אובייקטים "מרחפים" ללא references) אולם, ה-GC של פלאש פלייר מתקשה לנקות את כולם במכה אחת, ומה גם, שבשביל לשחרר אובייקט לניקוי (כלומר לנקות לו כל קשר לאובייקט אחר בכל צורה שתהיה) זו משימה עבור לוק סקיי ווקר עם הרבה force.
הכל מתחיל מגילוי של נזילת זכרון באפליקציה. זה לא מפתיע, בהתחשב בכך, שאחרי הכל, מדובר בפלאש פלייר שהוא נוזל כמו גג פחון במעברה, אבל אתם יודעים... אמרו שיש שיפורים, שיש מנגנון חדש ל GC, שאמור להיות טוב עכשיו. אם אדובה התכוונו בכך שאם פעם לא היה GC כלל והיום יש, הם צודקים - זה שיפור. אם הם מתכוונים לכך שאפשר לסמוך עליו, שיחזרו למעבדה, שכן אפליקציות עדיין נוזלות ונוזלות קשה. אני מדבר על אפליקציה שרצה לילה שלם ומגיע בקלילות לנפחים של 200-400 MB, ועולה. זה לגמרי לא קביל בעולם ה RIA, ואם אדובה מכוונים לשם, אז כדאי שיתחילו לשים לב לדברים ה"פעוטים" הללו, במקום לספק לנו עוד פילטר שיודע לעשות מימונה לפיקסלים.
אבל זכינו והגענו לזמן הזה, בו יש לנו פרופיילר שיודע לזהות נזילות זכרון ובעיות performance. אין ספק שהשימוש בפרופיילר הוא לא ממש אינטואיטיבי. יש בו רשימות על גבי רשימות של מידע, בו אפשר לעשות drill down, מה שהופך את כל השימוש בו למאד קשה, אלא אם מבודדים את הבעיה בדרכים ושיטות שונות.
הפרופיילר עובד מול SWF שמחזיק בתוכו מידע debug, וכמובן אל מול נגן שתומך ב debug אשר נותן להתחבר אל socket בו. אחרי שבודדנו את הצעדים שגרמו לדעתנו לנזילה ולקחנו כמה snapshots של מצבים והשוונו בניהם, הבחנו בכמה דברים מטרידים:
  • ה GC שנקראבאופן דיפולטי לפני snapshot לא מנקה את כל מה שהוא אמור. את זאת ניתן לבסס על העובדה שיש אובייקטים צפים שאין להם שם reference, ובכל זאת הם עדיין ברשימת האובייקטים המרחפים.
  • יש אינפלציה מאסיבית של אובייקטים מסוג Strings. רובם מוקצעים עבור אובייקטיי UID - זהו id ייחודי שהנגן נותן כמעט לכל אובייקט שנוצר בו אנחנו שמנו לב שעיקר הבעיה שלנו הייתה עם אובייקטים מסוג ArrayCollections).
זה נכון שניתן לפלטר על Packages שמעניינים אותנו, אבל אלו, למרבה הפלא, התנהגו בסדר. האובייקטים בהן טפחו אמנם, אבל נוקו ברגע... שהקומפיילר החליט לנקות אותם - בד"כ, כש Memory Allocation Block הגיע לשיאו ונדרש עוד מקום, שזה רעיון רע לכשעצמו, שכן אם לא מגיעים לשיא הזה, האובייקטים יכולים לחיות לנצח בזכרון למרות שאין להם שום reference.
רעיון הזוי הביא אותנו לנסות להריץ את האפליקציה על פלייר שאינו debug. ומסתבר שפה ממש קבור הכאלב. ריצה של לילה שלם הותירה את הפלייר שלנו די סטאטי מבחינה נפח הזכרון שהיא צרכה. מסתבר שיש הבדל מאד מהותי ומשמעותי בין שני סוגי הנגנים: אחד דולף כמו ח"כ בוועדה והשני רגוע.
זה מעט מטריד... טיפה. שכן הפרופיילר יכול לעבוד רק על... ובכן, גרסת ה debug. זה עקום ברמות די קשות, שאת בדיקות ה performance ניתן לעשות מול נגן שדולף כמו פסיכי (אני מדבר על הבדלים של 50MB בנקל בין שני הנגנים).שלחתי מכתב לגברת אדובה בנושא, ואני רוצה גם לבודד test בכדי לנסות ולהוכיח את ההשערות הללו בצורה נחרצת. מה שכן, כמה מסקנות עבורכם:
  • לבדיקות performance תוודאו שאתם עובדים מול פלייר שאינו debug.
  • כאשר עובדים עם הפרופיילר, כדאי לפלטר החוצה כל מה שאינו קשור במחלקות שלכם. השאר פשוט מקשה מאד ולפעמים מטעה מאד.
  • אי אפשר לסמוך על ה GC של פלאש.
  • אם אתם ממש רוצים להפעיל את ה GC של פלאש בצורה תכנותית, כפויה, שתנקה באמת את הכל (לא כמו בפרופיילר) יש "האק" נחמד שמצא אותו קולגה שלי בקישור הבא: נו באמת... שיהיה.
צריך להוסיף משהו? לא.... לא נראה לי. אני חושב שסיכמתי כאן בצורה די ממצה שבועיים של עצבים (פחות או יותר).
תודה, אדובה, על האפרת השיער.





6 comments:

עטר ש. said...

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

ronen said...

זה נכון ש GC לא רץ כל אימת שמתחשק למתכנת לקרוא ל GC אולם אחרי לילה שלם אובייקטים צפים אמורים להיות מנוקים בכל VM שמכבד את עצמו (ביננו זה לא שה VM מבצע פה Number crunchin) ועל כן אני מסכים עם הכתוב.

FlashMattic said...

עטר,
לא זקוקים לשלוש שנים במדעי המחשב בכדי לדעת שה GC אינו צפוי (אני מקווה עבורך...). מה שכן, ברב השפות ניתן לקרא באופן תכנותי ל GC לעשות את שלו, בלי hack מטורף כמו שמוצע לעיל.
אני מחזק את מה שרונן כתב; אפילו אם הוא לא צפוי, לפחות שברגע שהוא מתבצע, שינקה את הכל, או 90% מהכל... אבל שלא ישאיר אותנו עם 60% אובייקטים צפים. זה חוסר אחריות של אדובה.

Billigflüge said...

I am missing language pack here, but just read your post and have had a little argument with my collegue on that topic, cause I totally agree with you, but can´t convinced him of my opinion. Anyway I love your blog and appreciate it very much that you post your comments in english also. kind regards from germany now.

FlashMattic said...

תודה בנאדם - כיף לשמוע.
באיזו בעיה בדיוק אתה נתקל בטיעונים שלך אל מול הקולגה?

Tal said...

System.gc()