Presentation is loading. Please wait.

Presentation is loading. Please wait.

אופטימיזציות תלויות מכונה של תוכניות מבוסס על Bryant & O’hallaron / Computer Systems: a programmer’s perspective.

Similar presentations


Presentation on theme: "אופטימיזציות תלויות מכונה של תוכניות מבוסס על Bryant & O’hallaron / Computer Systems: a programmer’s perspective."— Presentation transcript:

1 אופטימיזציות תלויות מכונה של תוכניות מבוסס על Bryant & O’hallaron / Computer Systems: a programmer’s perspective

2 – 2 – עד עתה... המטרה חשב את סכום איברי הוקטור Vector represented by C-style abstract data type Achieved CPE of 2.00 CPE = Cycles per element void combine4(vec_ptr v, int *dest) { int i; int length = vec_length(v); int *data = get_vec_start(v); int sum = 0; for (i = 0; i < length; i++) sum += data[i]; *dest = sum; }

3 – 3 – נהפוך את הקוד ליותר כללי טיפוסים נשתמש בהגדרות שונות בשביל data_t int float double void abstract_combine4(vec_ptr v, data_t *dest) { int i; int length = vec_length(v); data_t *data = get_vec_start(v); data_t t = IDENT; for (i = 0; i < length; i++) t = t OP data[i]; *dest = t; } פעולות נשתמש בהגדרות שונות בשביל OP ו IDENT + / 0 * / 1

4 – 4 – אופטימיזציות לא תלויות מכונה אופטימיזציות הפחת גישות לזיכרון וקריאות לפונקציות בתוך הלולאה. משהו קורה כאן מבחינת ביצועים... מכפלה של FP איטית במיוחד. אבל הבעיה נפתרת כשמשתמשים במשתנה זמני... מדוע ? נובע ממוזרות מסוימת ב IA32 הזיכרון משתמש ב 64 ביט. גלישה מטופלת בתוכנה, ולכן איטית מאוד. הרגיסטרים משתמשים ב 80 ביט. יוצרים גלישה רק כשמועברים לזיכרון. הקלט שנבדק יצר גלישה ב 64 ביט, אבל לא ב 80 ביט. Integer Floating Point Method + * + * Abstract-g 42.06 41.86 41.44 160.00 Abstract-O2 31.25 33.25 31.25 143.00 Move vec_length 20.66 21.25 21.15 135.00 data access 6.00 9.00 8.00 117.00 Accum. in temp 2.00 4.00 3.00 5.00 התוצאה משתנה!

5 – 5 – שימוש במצביעים אופטימיזציה: מצביעים במקום גישות למערך. במקרה הזה מאפשר גם להיפטר מהמונה i. לא בהכרח עוזר. מאוד רגיש למבנה המדויק של המעבד. בדר"כ מהדרים טובים יותר באופטימיזציות של מערכים. void combine4p(vec_ptr v, int *dest) { int length = vec_length(v); int *data = get_vec_start(v); int *dend = data+length; int sum = 0; while (data < dend) { sum += *data; data++; } *dest = sum; }

6 – 6 – ארכיטקטורה של מעבד מודרני Execution Functional Units Instruction Control Integer/ Branch FP Add FP Mult/Div LoadStore Instruction Cache Data Cache Fetch Control Instruction Decode Address Instrs. Operations Prediction OK? Data Addr. General Integer Operation Results Retirement Unit Register File Register Updates Control Unit (CU) Arithmetic Logic Unit (ALU)

7 – 7 – היכולות של Pentium III ניתן להריץ במקביל: 1 load 1 store 2 integer (one may be branch) 1 FP Addition 1 FP Multiplication or Division Some Instructions Take > 1 Cycle, but Can be Pipelined InstructionLatencyCycles/Issue Load / Store31 Integer add11 Integer Multiply41 Integer Divide3636 Double/Single FP Multiply52 Double/Single FP Add31 Double/Single FP Divide3838 חלקים שונים של אותה פקודה מבוצעים על ידי רכיבים שונים. לכן ניתן לבצע מספר רכיבים של פקודות שונות בו זמנית.

8 – 8 – אופטימיזציה: פרישת לולאות (Loop Unrolling) אופטימיזציה: צרף מספר איטרציות לתוך גוף הלולאה. חוסך תקורה של הלולאה. pipelining של פקודת load – עד 3 פעולות. 'קשור קצוות' בסוף. Measured CPE = 1.33בנוסף: בלוק בסיסי עם יותר פעולות: יותר חופש פעולה למהדר לבצע אופטימיזציות. למשל: ביצוע במקביל של פעולות בסיסיות אם הן בלתי תלויות. void combine5(vec_ptr v, int *dest) { int length = vec_length(v); int limit = length-2; int *data = get_vec_start(v); int sum = 0; int i; /* Combine 3 elements at a time */ for (i = 0; i < limit; i+=3) { sum += data[i] + data[i+1] + data[i+2]; } /* Finish any remaining elements */ for (; i < length; i++) { sum += data[i]; } *dest = sum; }

9 – 9 – הצנרה (pipelining) של load ציר הזמן קיבלנו 3פעולות Load תוך 5 מחזורי שעון add Load

10 – 10 – ההשפעה של Unrolling בדוגמה שלנו עוזר רק לחיבור integers במקרים האחרים האלמנט הדומיננטי הוא ה latency של הפעולות (שקף הבא). ההשפעה היא לא ליניארית או מונוטונית. גורמים רבים משפיעים על השיבוץ בפועל של הפעולות. Unrolling Degree 1234816 IntegerSum2.001.501.331.501.251.06 IntegerProduct4.00 FPSum3.00 FPProduct5.00

11 – 11 – הצנרה (pipelining) של load ציר הזמן כאן הצנרה לא עוזרת... Load Multiply

12 – 12 – חישוב סדרתי חישוב ((((((((((((1 * x 0 ) * x 1 ) * x 2 ) * x 3 ) * x 4 ) * x 5 ) * x 6 ) * x 7 ) * x 8 ) * x 9 ) * x 10 ) * x 11 )ביצועים N elements, D cycles/operation N*D cycles * *1 x0x0x0x0 x1x1x1x1 * x2x2x2x2 * x3x3x3x3 * x4x4x4x4 * x5x5x5x5 * x6x6x6x6 * x7x7x7x7 * x8x8x8x8 * x9x9x9x9 * x 10 * x 11

13 – 13 – אופטימיזציה: פרישת לולאות מקבילית Parallel Loop Unrolling גרסה: הכפלת שלמים אופטימיזציה: סכום בשתי מכפלות בלתי תלויות. ניתן לביצוע במקביל. הכפל אותם בסוף.ביצועים: CPE = 2.0 המהירות הוכפלה (עבור מכפלתint ) void combine6(vec_ptr v, int *dest) { int length = vec_length(v); int limit = length-1; int *data = get_vec_start(v); int x0 = 1; int x1 = 1; int i; /* Combine 2 elements at a time */ for (i = 0; i < limit; i+=2) { x0 *= data[i]; x1 *= data[i+1]; } /* Finish any remaining elements */ for (; i < length; i++) { x0 *= data[i]; } *dest = x0 * x1; }

14 – 14 – חישוב שתי מכפלות במקביל חישוב: ((((((1 * x 0 ) * x 2 ) * x 4 ) * x 6 ) * x 8 ) * x 10 ) * ((((((1 * x 1 ) * x 3 ) * x 5 ) * x 7 ) * x 9 ) * x 11 )ביצועים: N elements, D cycles/operation (N/2+1)*D cycles ~2X performance improvement * *1 x1x1x1x1 x3x3x3x3 * x5x5x5x5 * x7x7x7x7 * x9x9x9x9 * x 11 * * *1 x0x0x0x0 x2x2x2x2 * x4x4x4x4 * x6x6x6x6 * x8x8x8x8 * x 10

15 – 15 – דרישות ליצירת ריצה מקבילית דרישות מתמטיות: הפעולות צריכות להיות אסוציאטיביות וקומוטטיביות מכפלת שלמים – בסדר. לא תמיד נכון עבור floating-point »בסדר ברוב האפליקציות. דרישות מהחומרה: Pipelined functional units המהדר + המעבד צריכים להיות מסוגלים לזהות את האפשרות למקביליות מתוך הקוד. נקרא out-of-order execution.

16 – 16 – כיצד נוצרת ריצה מקבילית ? ב IA-32 אין פקודות מפורשות שאומרות למעבד איזה פקודות ניתן לבצע באופן בלתי תלוי. ב IA-32 אין פקודות מפורשות שאומרות למעבד איזה פקודות ניתן לבצע באופן בלתי תלוי. ב IA-64 דווקא יש. למעבד יש יכולת מסוימת להסתכל קדימה – על 10~ הפקודות הבאות. למעבד יש יכולת מסוימת להסתכל קדימה – על 10~ הפקודות הבאות. יכול לעתים להבחין שיש פקודה בהמשך הדרך שאינה תלויה בתוצאה של החישוב שמתבצע כרגע. יכול לעתים להבחין שיש פקודה בהמשך הדרך שאינה תלויה בתוצאה של החישוב שמתבצע כרגע.

17 – 17 – Optimization Results for Combining

18 – 18 – שיטה #2Parallel Unrolling גירסת קוד: הכפלת שלמים אופטימיזציה הכפל זוגות, ואז עדכן והשלם תוצאה. “Tree height reduction”Performance CPE = 2.5 void combine6aa(vec_ptr v, int *dest) { int length = vec_length(v); int limit = length-1; int *data = get_vec_start(v); int x = 1; int i; /* Combine 2 elements at a time */ for (i = 0; i < limit; i+=2) { x *= (data[i] * data[i+1]); } /* Finish any remaining elements */ for (; i < length; i++) { x *= data[i]; } *dest = x; }

19 – 19 – שיטה #2 חישוב: ((((((1 * (x 0 * x 1 )) * (x 2 * x 3 )) * (x 4 * x 5 )) * (x 6 * x 7 )) * (x 8 * x 9 )) * (x 10 * x 11 ))ביצועים: N elements, D cycles/operation Should be (N/2+1)*D cycles CPE = 2.0 Measured CPE worse * * 1 * * * * * x1x1x1x1 x0x0x0x0 * x3x3x3x3 x2x2x2x2 * x5x5x5x5 x4x4x4x4 * x7x7x7x7 x6x6x6x6 * x9x9x9x9 x8x8x8x8 * x 11 x 10 Unrolling CPE (measured) CPE (theoretical) 22.502.00 31.671.33 41.501.00 61.781.00

20 – 20 – מקביליות CPE = 4.00 מכפלות מבוצעות באופן סדרתי /* Combine 2 elements at a time */ for (i = 0; i < limit; i+=2) { x = x * (data[i] * data[i+1]); } /* Combine 2 elements at a time */ for (i = 0; i < limit; i+=2) { x = (x * data[i]) * data[i+1]; } CPE = 2.50 חלק מהמכפלות - במקביל data[2]*data[3] במקביל ל X * (data[0]*data[1])

21 – 21 – מגבלות של ביצוע מקבילי דרושים רגיסטרים רבים כדי להחזיק תוצאות של מכפלות / סכומים. יש רק 6 רגיסטרים שמישים ל - Integers דרושים גם בשביל מצביעים, תנאי לולאה, וכו'. 8 רגיסטרים ל floating-point כשאין מקום ברגיסטרים, המידע מועבר לזיכרון הראשי (ל – stack). נקרא register-spilling מבטל את האפקטיביות של האופטימיזציה.

22 – 22 – סִכּוּם: תוצאות על Pentium III ההאצה הגדולה ביותר בשל האופטימיזציות הבסיסיות. אבל, גם ההאצות הקטנות חשובות.

23 – 23 – דומה ל Pentium 3 למרות שהמבנה הפנימי והמהדר שונים לחלוטין. סכום: תוצאות על מחשב Alpha

24 – 24 – תוצאות עבור Pentium 4 מהירות שעון גבוהה יותר 2.0 GHz לא נוצרת הבעיה בגלישה של FP.

25 – 25 – הסתעפויות בעיה ה CU חייב להיות לפני ה ALU (או ה execution unit) כדי לייצר משימות מספיק מהר כך ש ה ALU ינוצל באופן מקסימלי. מה ה CU צריך לעשות כשמגיע להסתעפות בתוכנית ? תנאי ההסתעפות טרם חושב! להיכן עליו להתקדם ? התשובה: חיזוי (branch predication) חיזוי יכול גם להיות שגוי... העלות של חיזוי שגוי: על pentium III: ~14 clock cycles

26 – 26 – לעתים ניתן להימנע מהסתעפויות... שימוש ב'מסכות' הרעיון הוא לנסות למנוע branch prediction במקומות בהם: נקרא הרבה פעמים אין עדיפות לכיוון מסוים של ה if (לכן בדר"כ לא רלבנטי ללולאות שם יש עדיפות). העלות של branch miss גבוהה מדי. int bmax(int x, int y) { int mask = -(x>y); return (mask & x) | (~mask & y); }

27 – 27 – לעתים ניתן להימנע מהסתעפויות... שימוש ב'מסכות' במקרה הספציפי הזה gcc הופך את זה חזרה ל if... ניתן לפתור על ידי הכרזת volatile int mask. מכריח את mask להישמר בזיכרון ולא ברגיסטר. מכריח את המעבד לחשב את הערך לפני שממשיך. תקורה: 22 clock cycles. int bmax(int x, int y) { volatile int t = (x>y); int mask = -t; return (mask & x) | (~mask & y); }

28 – 28 – לסיכום: אופטימיזציות תלויות מכונה Loop Unrolling חלק מהמהדרים עושים זאת אוטומטית בדר"כ באופן ידני ניתן לעשות טוב יותר מקביליות (Instruction-Level Parallelism) מאוד תלוי מכונה

29 – 29 – תפקידו של המפתח... כיצד אכתוב את התוכנית שלי, כשיש לי מהדר טוב ? שמרו על איזון בין יעילות לקריאוּת ויכולת תחזוקה כמו-כן: בחרו באלגוריתם הטוב ביותר. כתבו קוד קריא וניתן לתחזוקה. הימנעו ככל האפשר מ'חוסמי אופטימיזציות' כך תאפשרו למהדר לעשות את העבודה שלו. הדגש הוא על לולאות פנימיות בצעו אופטימיזציות חזקות ככל האפשר – כאן הסיכוי הגדול היותר להאצה.


Download ppt "אופטימיזציות תלויות מכונה של תוכניות מבוסס על Bryant & O’hallaron / Computer Systems: a programmer’s perspective."

Similar presentations


Ads by Google