Premature optimization is the root of all evil

אני אוהב משפטי "root of evil". צביעה של עולם בשחור ולבן מקלה על החיים.למרבה הצער- העולם הוא לא באמת שחור ולבן.אפילו הביטוי הכול כך החלטי :

"premature optimization is the root of all evil"
שנהגה על ידי Donald Knuth ומשמעו:

"Premature optimization" is a phrase used to describe a situation where a programmer lets performance considerations affect the design of a piece of code. This can result in a design that is not as clean as it could have been or code that is incorrect, because the code is complicated by the optimization and the programmer is distracted by optimizing."
אינו כה חד מבחינת המקורות שלו.הציטוט הוא חלקי ביותר ולא משקף במאה אחוז את הקונטקסט השלם.הציטוט הינו מתוך מאמר שפורסם ב-1974 על ידי KNUTH והמשפט המלא הוא:

"The conventional wisdom shared by many of today's software engineers calls for ignoring efficiency in the small; but I believe this is simply an overreaction to the abuses they see being practiced by pennywise-
and-pound-foolish programmers, who can't debug or maintain their "optimized" programs. In established engineering disciplines a 12% improvement, easily obtained, is never considered marginal; and I believe the same viewpoint should prevail in software engineering. Of course I wouldn't bother making such optimizations on a oneshot job, but when it's a question of preparing quality programs, I don't want to restrict myself to tools that deny me such efficiencies.
There is no doubt that the grail of efficiency leads to abuse. Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil.

כמו כן 12 שנים אחר כך בראיון שנתן עידן KNUTH  את הביטוי.אך היסטוריה היא לא מטרתנו כאן.
גם החלטיות היא לא מטרתנו כאן, אבל כדי לא לפתוח כל מיני סוגריים ותתי סוגריים והסתייגויות, אנסה להתייחס לרעיון הכללי וכל דבר ספציפי כמובן מצריך את ההתייחסות שלו.
אופטימיזציה של מערכת נעשית בשלבים שונים של המערכת:
1.      שלב הארכיטקטורה.
2.      בחירת טכנולוגיות
3.      Height level design
4.      Low level design
5.      Implementation
6.      Bug fixes
בכל שלב יש להשקיע כמות שונה של זמן לגבי אופטימיזציה.
בניית ארכיטקטורה שגויה היא תקלה שקשה להתגבר עליה ולכן מין הסתם כמות המחקר לגבי אופטימיזציה שצריך להשקיע בשלב הזה גדולה הרבה יותר משלב המימוש.

יש כאלה שעל מנת להבחין בהתייחסות לשלבים השונים ממקדים את הציטוט לעיל לכדי הציטוט הבא:

"premature optimization is the root of all evil  micro".

הסבר שאהבתי שמתייחס לציטוט המקורי הוא:
in the absence of measured performance issues you shouldn't optimize becuase you think you will get a performance gain. There are obvious optimizations (like not doing string concatenation inside a tight loop) but anything that isn't a trivially clear optimization should be avoided until it can be measured.
בואו ננסה לצלול לציטוט מתוך דוגמה אמיתית:
Java formatter הוא אובייקט " יקר " ( מה זה יקר? – יקר יחסית לאוביטים אחרים ) ואינו thread safe, לכן מתכנת החליט שהוא יצור – ThreadLocalFormat כדי לעטוף את ה- formatter .
אם נריץ TEST שבודק מהו יקר? נקבל במחשב ממוצע יצירה של 200,000 אוביטים בשנייה.
זה מתכנת שעשה premature . נכון, הוא חסך זמן מחשב, אך האם הוא בכלל היה צריך לחסוך זמן מחשב? האם ב- USE CASE  הספציפי שלו זה מה שקריטי?
זוהי דוגמה שלפני שעשית over design למקום שיכל להיות פשוט.מערכת תוכנה היא מערכת מסובכת מעצם טיבעה. אין טעם להוסיף סיבוך נוסף היכן שלא צריך. במידה ואינך יודע בוודאות כי כתיבה מסוימת עלולה לעלות ב- performance  יקר (אם אינך יודע בדוק לפני) אל תכניס אופטימיזציות. הם רק יסבכו אותך אחר כך בתחזוקה ובdebug . ראיתי מתכנתים שעבדו ימים על גבי ימים כדי לבצע סוג מסויים של פילטור נתונים מתוך רשימה בכמה שפחות מעברים על הרשימה. הם השקיעו ימים כדי להגיע לאלגוריתמים הכי טוב וכזה הגיע אלי השאלה הראשונה שלי הייתה למה לא לעבור על הרשימה 6 פעמים ולא 2 כפי שתכננתם אבל בצורת קוד יותר ברורה?
התשובה שלהם הייתה שמעבר על הרשימה 6 פעמים זה בזבוז שכן בצורה חכמה ניתן להוציא את המידע על ידי שימוש בכל מיני טריקים ורק ורסיות בפעמיים בלבד.
כששאלתי מה ה- impact של לעבור עליה 6 פעמים ולא 2 (נניח כרגע את הרק ורסיות בצד) התשובה הייתה – מדובר בפי 3 זמן.
תשובה זאת אינה מספקת, שכן פי X זמן אינו מדד לכלום. מה אם אגיד לכם שאני יכול לייעל חלק מסוים בתוכנה שלכם שיעבור במאית מהזמן הקודם או שתעדיפו שאייעל חלק אחר שיעבור ב-99 אחוז מהזמן הקודם? אני בטוח שכולנו לא נסכים להצעה כזאת בצורה עיוורת ונשאל מה ה- IMAPCT על התוכנה כולה. בכמה זמן יורד אותו USE CASE ? בכמה יורד חתימת הזיכרון ? בכמה יורד בעיות של contentions ? ...
פי X אינו מדד לכלום.
כנראה שהאוניברסיטה (קורסים לחישוביות ואלגוריתמים ) השפיעו רבות על כולנו וההסתכלות של רבים על עולם התוכנה היא סביב חסם ה- O (http://he.wikipedia.org/wiki/סיבוכיות). כמעט בכל ראיון עבודה ישאלו את המתכנת שאלת סיבוביות , בכמה מעברים מינימאליים תוכל לעשות זאת? בפוסט הבא מתוארת סוג כזאת של שאלה http://j2ee-now.blogspot.com/2007/12/google-interview-questions-part-1.html כפי שנשאלת בראיונות של גוגל. תגובה מעניינת שיש שם היא תגובתו של Riccardo:
That's the typical kind of problem you forget how to solve when you work for years using high level frameworks (in any language); in my career I have never seen a case where a problem of this complexity had to be solved.
מניסיוני ראיתי הרבה מקרים של התעסקות באופטימיזציה באופן שהוא Premature כלומר נטול הוכחות לכך שיש כאן בכלל צורך של אופטמיזיציה. הטענות של המתכנתים הן בסגנון – זה יותר מהר פי X, מה אם ברשימה יהיו מיליון רשומות ? , מה אם יהיו מיליון משתמשים שירצו את אותו מידע... כתיבה נכונה של קוד בצורה של בלוקים שניתנים בקלות לפירוק והרכבה אחרת (Refactor) נותנת לכם את היכולות להתמודד עם הבעיות במציאות ולא בתאוריה. אין צורך לתכנן אלגוריתמים יעיל על סמך כך שברשימה יהיו מיליון רשומות. יש צורך לתכנן אותו על סמך כמה שיש בפועל ולא בתיאוריה.
שימו כל הזמן מול עיניכם שאנו מדברים היום במעבדים שמצבעים מיליארדי פעולות בשנייה. מעבר של 5 או 6  פעמים או אפילו 10 על רשימה לא יהיה מה שישנה להם. לכן אל תזדרזו לעשות אופטימיזציה ללא שאבחנתם שיש בעיות או ברור לכם שיש בעיות (premature).
ובכלל כדאי לבחון מחדש כל מיני דיעות קדומות שלנו לגבי ביצועים. אמונות שנשענות על בסיס בעיות שהיו בעבר ולא קיימות כבר היום לדוגמה – שאלו את עצמכם את אותה שאלה שנשאלתם בראיונות לגבי ביצועים של HashTable  ?
מרבית מאמרי ה- Performance וטיפים לגבי Micro Performance נעשו סביב JVM ישנים. הספר האחרון שעסק בתחום של MIcro Performance יצא במהדורה שנייה עבור JDK 1.4 .  מאז נעשה שיפור דרמטי בנושאי ביצועים כך שהרבה טיפים לא רלוונטים עוד.
ה- Optimizer של ה- JVM מסוגל היום לניסים בעיקר בזכות JIT עד כדי כך שהביצועים יכולים לעלות על ביצועי קוד שנכתב ב- C. שיפורי ביצועים בכל האזורים החל מ- IO וכלה בנושאי Concuurency הביאו לשיפור של לפעמים פי כמה אלפים יותר מהיר בין JDK 1 לבין הגירסה הנוכחית. יש לשים לב שכדי לחוות חלק מהאופטימיזציות יש " לחמם " את ה- JVM.  כאשר אתם בודקים ביצועים קחו זאת בחשבון.ניתן לראות דוגמה בפוסט הבא  - http://avihai-java.blogspot.com/2010/05/arraylist-vs-array-as-sample-to.html
לפוסט יותר מפורט על JIT:
http://www.ibm.com/developerworks/java/library/j-jtp12214/

כדאי לקרוא את הפוסט הבא שמדגים שיקולים רבים שעלינו לשקול מבחינת בדיקת ביצועים נכונה:
http://www.ibm.com/developerworks/java/library/j-benchmark1.html



אין תגובות:

הוסף רשומת תגובה