איך לגרום לקוד ה-MATLAB שלכם לרוץ מהר יותר?
כתבתם אלגוריתם שעושה את העבודה בצורה טובה – אבל לו רק הייתה דרך לגרום לו לרוץ מהר יותר, הכל היה הרבה יותר ישים!
בפוסט זה נסקור את הטכניקות בהן ניתן להשתמש על מנת להאיץ את אלגוריתמים ואפליקציות שפיתחתם ב-MATLAB.
בין היתר נעבור על נושאים כגון:
- כיצד להעריך את ביצועי הקוד?
- שיטות ששווה לאמץ על מנת לכתוב בצורה יעילה יותר קוד טורי
- עבודה בגישה של OOP ושימוש ב-System Objects
- מינוף משאבי החומרה שבידנו, ויישום עיבוד מקבילי על CPUs ו-GPUs
- ייצור קוד C
הערכת ביצועי הקוד
לפני שבכלל נבצע שינויים בקוד, קודם עלינו לקבוע היכן למקד את מירב המאמצים.
שני כלים קריטיים לתמיכה בתהליך זה הם ה-Code Analyzer ו-MATLAB Profiler.
ה-Code Analyzer נמצא על גבי ה-MATLAB Editor, והוא מבצע בדיקות על הקוד בצורה דינאמית תוך כדי שהקוד נכתב. הוא מנתח את הקוד, מזהה בעיות פוטנציאליות, ובהתאם ממליץ על שינויים כדי למקסם את הביצועים ואת תחזוקת הקוד.
ניתן להריץ דוחות וניתוחים של ה-Code Analyzer על גבי תיקיות שלמות, מה שמאפשר להציג את כל המסקנות של ה-Code Analyzer עבור קבצים רבים, תחת מסמך אחד.
בעזרת MATLAB Profiler, ניתן לגלות בצורה אינטרקטיבית, נוחה וגרפית את זמני הביצועים של הקוד, מה שעוזר לנו לחקור את הקוד ולראות היכן טמונים צווארי הבקבוק בקוד – ובהתאם, לשנות את אותם המקטעים הבעייתיים.
הכלי מפיק דוח שמסכם את ביצועי הקוד, כולל רשימה של כל הפונקציות שבוצעו אליהן קריאות, מספר הפעמים שכל פונקציה נקראה, והזמן הכולל שהוקדש לכל פונקציה.
כמו כן הכלי מספק גם מידע על התזמון הפנימי בכל כל פונקציה, כגון אילו שורות קוד דורשות את זמן העיבוד הרב ביותר.
אחרי שהצלחנו לזהות את צווארי הבקבוק, נוכל להתמקד בשיטות לשיפור הביצועים של מקטעים ספציפיים אלו בקוד. לאחר שניישם את השיפורים שלנו, ה-Profiler יוכל לעזור לנו למדוד כמותית את השיפור בזמנים.
אימוץ שיטות לכתיבה יעילה של קוד טורי
לפני שניגש לשיטות מתקדמות להאצת ביצועים של הקוד שלנו, כדאי שראשית ניישם שיטות שונות לאיפטום הקוד, כך שירוץ בצורה המהירה ביותר באופן טורי.
שתי טכניקות תכנות יכולות תכנות יעילות לשם כך הן ווקטוריזציה (Vectorization) ופרה-אלוקציה (Pre-allocation).
פרה-אלוקציה הכוונה לאתחול המערך באמצעות הקצאה של הגודל הסופי הנדרש עבור אותו מערך. יישום הקצאת הזכרון מראש עוזרת להימנע משינוי דינמי של גודל מערכים, במיוחד כאשר הקוד מכיל לולאות for ו-while.
מכיוון שמערכים ב-MATLAB מוחזקים בגושי זיכרון רציפים, שינוי גודלם של מערכים לעתים קרובות מחייב את MATLAB לבזבז זמן בחיפוש אחר בלוקים רציפים גדולים יותר של זיכרון ואז להעביר את המערך לאותם בלוקים. על ידי ביצוע הקצאה מראש של מערכים, ניתן להימנע מפעולות זיכרון מיותרות אלו ולהביא לשיפור בזמן הביצוע הכולל.
ווקטוריזציה הוא תהליך המרת הקוד משימוש בלולאות לשימוש במטריצות ובפעולות ווקטוריות.
MATLAB הינה שפה שנוצרה במקור לחישוב בעיות של אלגברה לינארית, והיא אופטימלית לחישובים וביצוע מניפולציות שונות על מטריצות ווקטורים.
לכן, לעיתים קרובות ניתן לשפר את הביצועים של הקוד על ידי ביצוע ווקטוריזציה של הקוד.
ניקח בחשבון שחישובים הכוללים ווקטורים גדולים ומערכי ענק יכולים להיות מועמדים טובים להאצה באמצעות GPU. במקרים בהם לא ניתן לעשות ווקטוריזציה של לולאות for, לעתים קרובות נוכל להחליף את לולאת ה-for בלולאת for מקבילית (parfor), וכן נוכל גם להחליף את מקטעי קוד ה-MATLAB בקוד C, בכדי להאיץ את האלגוריתם.
תוכלו לעיין בסעיפים על מחשוב מקבילי ויצירת קוד C מ-MATLAB לפרטים נוספים על טכניקות אלו.
עבודה בגישה של OOP ושימוש ב-System Objects
ניתן להשתמש ב-System Objects כדי להאיץ את הקוד שלנו.
System Objects הם אובייקטים שנכתבו ב-MATLAB בגישת Object-Oriented Programming (OOP). למעשה מדובר בקלאסים (Classes) שעל ידי שימוש בהם ניתן לבצע את האלגוריתמים בצורה מהירה יותר. הם מתוכננים במיוחד להטמעה ולריצה בסימולציות במערכות דינאמיות עם קלטים שמשתנים לאורך הזמן.
אובייקטים אלו אידאליים עבור חישובים איטרטיביים אשר מעבדים כמויות גדולות של דאטה, וכן בעלי היכולת לעבד נתונים שמגיעים באופן רציף ((Streaming. היתרון המובהק שמתקיים עקב עובדות אלו, הוא שלא צריך להחזיק כמויות גדולות של נתונים בזיכרון כלל, בכל שלב על גבי שולחן העבודה שלנו.
בנוסף ל-System Objects שמגיעים ביחיד עם הכלים השונים של MATLAB ו-Simulink, ניתן ליצור System Objects חדשים משלנו.
מינוף משאבי החומרה שבידנו, ויישום עיבוד מקבילי על CPUs ו-GPUs
עד כה תיארנו דרכים לאפטם את הקוד שלנו בצורה טורית. ניתן גם להשיג האצה בביצועים על ידי ניצול כל המשאבים ולמנף את היכולות של החומרה שבידנו באופן מקסימלי.
באמצעות MATLAB וSimulink- ניתן לבצע חישובים בקנה מידה גדול, לבצע סימולציות ולפתור בעיות עתירות נתונים זאת על ידי שימוש במעבדים מרובי ליבות (Multi-core), מעבדים גרפיים (GPUs) וכן על גבי קלאסטרים (Clusters) של מחשבים.
באמצעות Parallel Computing Toolbox ניתן לשלוט על CPUs GPUs מרובי ליבות לוקליים על מנת להאיץ את עבודתנו. באמצעות יכולת זו, ניתן לנצל את כוח העיבוד המלא של משאבי המחשוב הלוקליים שברשותנו, תוך כדי דאגה לביזור היישומים שיופעלו מ-MATLAB על גבי Workers – בשפה של MathWorks, הכוונה ל MATLAB computational engines-שרצים לוקלית במחשב שלנו במקביל.
הידע שנדרש לשם כך הוא מינימלי – אין צורך בידע מקדים בכתיבת קוד CUDA או תכנות ב-MPI לצורך ביצוע המיקבול על גבי ה-MATLAB או להריץ סימולציות שונות של Simulink במקביל.
MATLAB Parallel Server מאפשר ביצוע scaling up ליישומים שנבנו ב- MATLABולסימולציות בSimulink- לקלאסטרים מרוחקים ולענן.
באמצעות יכולת זו, ניתן לבנות אבטיפוס של היישומים והסימולציות הקיימים על גבי שולחן העבודה, ולאחר מכן לבצע scaling up לקלאסטרים ולענן – עם שינוי קוד מינימלי בלבד.
למעשה, כלי זה דואג להריץ את התוכניות והסימולציות הקיימות כיישומים מתוזמנים מבלי שנצטרך לדאוג מהבדלים במערכות ההפעלה, סביבות שונות ובמתזמנים.
הכלי משתמש לשם כך במתזמן המאופטם של MATLAB (Job Scheduler)שמסופק על ידי הכלי, או שניתן גם לבחור במתזמן משלכם.
ייצור קוד C
ישנם מקרים בהם ווקטוריזציה אינה אפשרית, או שתיקח זמן רב. במקרים כאלה, ייצור קוד C בצורה אוטומטית והחלפת מקטעים מקוד ה-MATLAB יכולה להביא להאצת מהירות הביצוע של הקוד.
באמצעות כלי ה-MATLAB Coder, ניתן ליצור קוד C קריא ונייד, לאחר מכן ניתן לקמפל אותו לפונקציית MEX אשר מחליפה את החלק המקביל באלגוריתם ה-MATLAB. קוד MEX הוא למעשה קוד C אשר עטוף במעטפת המאפשרת להאיץ את הקוד ישירות מ-MATLAB.
לסיכום, בכל אחת מהשיטות השונות קיים פוטנציאל לעזור לנו להשיג האצה בזמני הריצה של הקוד, ובמקרים רבים גם נוכל לנקוט בשילוב בין השיטות השונות שפורטו לעיל על מנת להאיץ באופן משמעותי יותר את הקוד שברשותנו.