מי שמוריד את סביבת הפיתוח הרשמית והחינמית MPLAB X, של חברת Microchip, מקבל איתה בין השאר את הקומפיילר XC8 בגרסה שמבצעת (כך מספרים לנו) רק אופטימיזציות בסיסיות. כדי להפיק קוד יעיל באמת, הלקוחות נדרשים לקנות גרסה משופרת. עד כאן סביר ולגיטימי, נכון?
אבל איזה מעשה מפוקפק עושה Microchip כאשר ההבדל בביצועים בין הגרסה הרגילה למשופרת לא נראה גדול מספיק כדי להצדיק את ההוצאה הכספית?
הקדמה: אופטימיזציה אוטומטית
כשאני מדבר על אופטימיזציה בבלוג הזה, הכוונה בדרך כלל למציאה של דרכים חדשות וחכמות יותר לבצע משימות מורכבות. הדבר נעשה ברמת חשיבה גבוהה ומופשטת יחסית, הרבה יותר ממה שמחשבים – בימינו, לפחות – מסוגלים לבצע. עם זאת, יש לא מעט שיפורים "בנאליים" שאפשר להכניס גם ברמות הנמוכות יותר של התוכנה, וקומפיילר ראוי לשמו יודע כיצד לעשות זאת.
לדוגמה, נניח שמתכנת כותב לולאה שעושה משהו עשר פעמים, באמצעות מונה שעולה מ-1 עד 10. נניח גם שהמונה הזה לא קשור למה שקורה בתוך הלולאה עצמה. בפענוח "טיפש", קומפיילר עשוי לממש את לוגיקת הלולאה בעזרת שלוש פעולות: 1) להגדיל את המונה באחד, 2) להשוות אותו לערך המקסימלי, 3) אם הוא גדול יותר, לצאת מהלולאה.
אבל יש דרך יעילה יותר: לספור לאחור. היא מסתמכת על העובדה שבדרך כלל, כאשר משתנה פנימי מסוים הופך להיות אפס, עולה אוטומטית "דגל" במערכת שאפשר לבדוק ישירות. לוגיקת הלולאה מצטמצמת לפיכך לשתי פעולות בלבד: 1) להקטין את המונה באחד, 2) אם הוא אפס, לצאת מהלולאה.
גם כאן היה צורך, כמובן, באדם כלשהו שיעלה את הרעיון הראשוני – אבל מרגע שהרעיון עלה אפשר היה לשלב אותו בכל קומפיילר כך שיבוצע אוטומטית כאשר התנאים מתאימים. הוסיפו לכך עשרות או מאות רעיונות דומים (או משוכללים יותר) ותבינו איך מגיעים לשיפור של עשרות אחוזים ביעילות הקוד. השאלה היא, כמה עשרות אחוזים יידרשו כדי לשכנע אתכם להוציא כמעט אלף דולר על הקומפיילר הטוב-יותר?
להכניס את העז
במקרה של XC8, כנראה שלדעת Microchip השיפור לא היה משכנע מספיק – ומכאן, לא רווחי מספיק – ולכן הם עשו "אופטימיזציה" של הקומפיילר בכיוון ההפוך: הגרסה החינמית מכניסה לקוד הוראות מיותרות, כדי להפוך אותו לפחות יעיל!
אני מסתמך כאן על הממצאים והפרשנות של רמירו פארחה (Pareja, אני מקווה שאני קורא זאת נכון) בפוסט שכתב בבלוג Tech for Fun בתחילת השנה [עדכון סוף 2020: הבלוג לא קיים יותר]. רמירו, שמכיר בין השאר את האסמבלי של מיקרו-בקרים ממשפחת PIC, חפר בקוד האסמבלי ש-XC8 הפיק מקוד C שכתב והזדעזע לגלות שבכל פעולת השמה ("="), הקומפיילר דחף לאסמבלי שתי פקודות מיותרות לחלוטין, להעתקה של ערך מרגיסטר מסוים למשתנה ומיד בחזרה. באנלוגיה, זה כמו להיכנס לאוטו, להתניע, לכבות, להתניע שוב ורק אז לצאת לדרך.
למען הסר ספק, הפעולות האלה לא משרתות שום צורך אמתי נסתר: עובדה שבגרסה הממוטבת, שמופקת על ידי הקומפיילר הקנוי, הן לא מופיעות.
כדי לוודא את העניין, בחנתי את האסמבלי של תוכנת אח הלדים שלי. הנה קטע מהפלט. השורות שממוספרות 36, 37 וכו' הן קוד המקור ב-C, והשורות ביניהן הן התרגום לאסמבלי. האם אתם יכולים להבחין בפקודות המיותרות?
עז זה כשר?
חברות מסחריות צריכות להרוויח כדי להתקיים, וכשמדובר במוצרים עם גרסאות חינמיות, הנוהג של פגיעה מכוונת ביכולות של הגרסאות האלה מקובל למדי: למשל, הרבה מאד תוכנות חינמיות מפסיקות לעבוד או מתחילות להציג פרסומות אחרי זמן מסוים, או שהן מגבילות את המשתמש בגודל הקבצים שהוא יכול לעבד וכדומה.
הבעיה במקרה הזה היא חוסר השקיפות. באתר של הקומפיילרים של Microchip עניין הפגיעה המכוונת לא מוזכר בשום צורה, והכל מנוסח ליצירת רושם כאילו הגרסה החינמית פשוט עושה מעט אופטימיזציות, בניגוד לגרסאות הקנויות שעושות הרבה.
בפוסט שהובא לעיל מוזכרת אנטי-אופטימיזציה נוספת, שהוסרה מהקומפיילר ביולי 2013. ייתכן שעם הזמן – אולי עם הופעתם של שיפורים נוספים בגרסאות הקנויות – גם העז הזאת תורחק בשקט-בשקט ותיעלם. למעשה, ייתכן שהיא כבר הורחקה בגרסה החדשה של הקומפיילר, שעוד לא התקנתי (שווה לבדוק בהזדמנות). עד אז, זכרו: אם הקוד שלכם ל-PIC 8-bit רץ לאט, יכול להיות שזה מפני שמישהו רצה שהוא ירוץ לאט…
הי עידו
למה ספירה לאחור יותר טובה ממונה שעולה כל פעם? בתכלס בשתי האופציות מגדילים/מקטינים את המספר, משווים לאפס/ערך מקסימאלי/בודקים דגל. אם כן הלולאה נגמרת, נראה אותם פעולות.
החיסכון מתבטא ברמה של הפעולות הכי בסיסיות. בספירה כלפי מעלה עד ערך שרירותי, אתה כל פעם: מעלה באחד – משווה לערך הרצוי – קופץ לפקודה המתאימה לפי התוצאה, כלומר שלוש פעולות בכל סיבוב. בספירה כלפי מטה, הדגל "אפס!" עולה אוטומטית כאמור כשמגיעים לאפס, אז הצטמצמת לשתי פקודות בכל סיבוב: מוריד באחד – קופץ לפקודה המתאימה. מה שאולי חסר כדי להבין את התמונה זה שפקודות תנאי באסמבלי מסתכלות ישירות על הדגלים. זאת אומרת, גם כשאתה משווה בין שני ערכים, התוצאה עמוק במעבד היא שאיזה דגל קופץ או לא קופץ, ואתה יכול לכתוב פקודת קפיצה שמסתמכת על הדגל. את הדגל עצמו לא… לקרוא עוד »
אני בדיוק שוקל האם לקנות PIC או ATtiny לפרויקט,
משום שattiny קטן לי מדי ויהיו לי פחות אפשרויות אבל PIC..
אין לי מושג איך הוא עובד וכו' האם הוא צריך קריסטל חיצוני או משהו בסגנון? גם הבנתי שאפשר דרך קומפלייר אחד לתכנת לו בקוד Arduino רגיל לחלוטין, האם הוא יעבוד אותו הדבר?
קודם כל יש הרבה סוגים של ATtiny, כולל עם 14 רגליים. יש קבצים שאפשר להוריד לסביבת הפיתוח כדי לתכנת ATtiny עם פונקציות של ארדואינו, אבל זה לא עובד במאה אחוזים כי הארדואינו תוכנן ספציפית ליכולות של סדרת ATmega ב-16MHz. ה-PIC זה משהו אחר, אני לא מכיר קומפיילר תואם-ארדואינו בשבילו – הוא מחייב כלים אחרים לגמרי. מה שלא תבחר, חשוב לזכור שברגע שאתה עוזב את הלוח הסטנדרטי של הארדואינו, דברים משתנים. זו כבר לא רק שאלה של איזה ג'וק לקחת – צריך ללמוד איך הוא עובד באמת. אם עוד לא קראת, אתה יכול לחפש כאן בבלוג (תחת "קטגוריות" בצד שמאל) רשומות… לקרוא עוד »
לא ידעתי שיש ATtiny עם 14 רגליים 😀 ה-84, מצויין 😉
לגבי התכנות, קראתי את הסדרות הלו טיני הלו פיק,
מעניינות מאוד אך אין הרבה קוד או הסבר על איך התכנות שלהם עובד, הרעיון הוא פשוט שאם למשל אני עושה פרויקט,
קודם כל בUno ואז אם אני רוצה לעבור למשהו חיצוני/נייד להעביר את הכל לATtiny, אז אתה אומר שהקוד לא ירוץ בדיוק אותו הדבר?
חיפשתי לגבי הPIC ויש מאמר לגביו-
שאומר שאפשר להשתמש בMPLAB IDE בשביל לתכנת כמו ארדואינו.
(http://circuitcellar.com/cc-blog/execute-open-source-arduino-code-in-a-pic-microcontroller-using-the-mplab-ide/)
שים לב שהמאמר הזה מדבר על דגם אחד ספציפי של PIC (אותו גודל כמו השבב שבארדואינו, יותר חזק וגם יותר יקר). מעבר לזה, הגישה שלי היא שאם כבר עוברים לכלי אחר, עדיף ללמוד ולנצל את מלוא היכולות שלו ולא להיצמד לשיטות שמיועדות למתחילים על פלטפורמה אחרת…
האם קוד לארדואינו ירוץ על ATtiny (כמובן אחרי שגילית מי זה כל פין ואיפה הוא נמצא) – תלוי עד כמה הקוד מורכב. תוכנית קטנה עם digitalRead ו-digitalWrite תעבוד; ספריה חכמה שנוגעת ישירות ברגיסטרים לא תעבוד, או תגרום לשגיאות שיקצרו לך את החיים.
מסכים איתך לגבי העניין שאם כבר עוברים, עדיף ללמוד עליו,
כמובן שאני יעשה את זה ברגע שהוא יהיה אצלי פיזית 😉
אך בנתיים, לא ממש מצאתי דף reference או משהו בסגנון על פונקציות ופעולות עליו.
אחרי שאתה כבר יודע על הבעיה הזו, אפשר פשוט למחוק את הפקודות המיותרות באופן עצמאי?
לא, כי אסמבלי עושה שימוש מאסיבי בכתובות של פקודות בזיכרון, ומחיקה פשוטה תשבש את הסדר. אפשר בתיאוריה לארגן מחדש את כל הקוד, אבל זה מורכב *מאד*.