C7_programming

Download Report

Transcript C7_programming

‫אופטימיזציה של תוכניות‬
‫מבוסס על ‪Bryant & O’hallaron / Computer Systems: a programmer’s perspective‬‬
‫הנושאים במצגת זאת‪:‬‬
‫‪‬‬
‫אופטימיזציות לא תלויות‪-‬מכונה‬
‫‪ ‬הזזת קוד‬
‫‪ ‬החלפת אופרטורים 'יקרים'‬
‫‪ ‬צירוף תתי ביטויים משותפים‬
‫‪‬‬
‫כוונון בעזרת ‪:profiling‬‬
‫‪ ‬זיהוי צווארי בקבוק בזמני ריצה‬
‫סיבוכיות‪ :‬תיאוריה אל מול מציאות‬
‫בדר"כ אנחנו מודדים סיבוכיות אסימפטוטית של אלגוריתמים‪.‬‬
‫בפועל‪ :‬אנחנו עובדים על תוכנה שלמה‪ ,‬לא רק אלגוריתם מבודד‪.‬‬
‫ניתן לשפר בקבוע – גם זה חשוב!‬
‫‪‬‬
‫‪‬‬
‫תכנות יעיל יכול להביא לשיפור ביצועים של פי ‪ 10‬ויותר‪.‬‬
‫אופטימיזציה במספר רמות‪ :‬אלגוריתם‪ ,‬מבנה נתונים‪ ,‬פרוצדורות‬
‫ולולאות‪.‬‬
‫לא ניתן לעשות זאת ללא הבנה של איך המחשב עובד‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫כיצד תוכנה מהודרת ומקושרת )‪(linked‬‬
‫איך למדוד ביצועי תוכנה ולזהות צווארי בקבוק‬
‫איך לשפר קוד בלי להרוס את המודולריות שלו והכלליות שלו‪.‬‬
‫–‪–2‬‬
‫סוגי אופטימיזציות‬
‫שתי משפחות של אופטימיזציות‪:‬‬
‫‪ ‬אופטימיזציות לא תלויות מכונה (הרצאה זאת)‬
‫‪ ‬אופטימיזציות תלויות מכונה (שעור הבא)‬
‫‪‬‬
‫האפקטיביות מאוד תלויה במבנה הספציפי של המעבד‬
‫–‪–3‬‬
‫מהדרים הם מתרגמים מרובי אופטימיזציות‬
‫ממפים ביעילות תוכניות לשפת מכונה‬
‫‪‬‬
‫‪‬‬
‫הקצאת רגיסטרים‬
‫אופטימיזציות 'זולות' לחישוב‪ ,‬מקומיות ושמרניות‪.‬‬
‫בדר"כ לא יכולים להשפיע על סיבוכיות אסימפטוטית‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫עדיין באחריות המתכנת לבחור את האלגוריתם הטוב ביותר‬
‫סיבוכיות )…(‪ O‬עדיין יותר חשובה משיפורים בפקטור קבוע‪.‬‬
‫אבל גם אלה חשובים‪...‬‬
‫מרכיבים רבים בקוד לא מאפשרים להם לעשות אופטימיזציות‬
‫מרחיקות ראות‪.‬‬
‫‪‬‬
‫‪‬‬
‫חשש מ ‪memory aliasing‬‬
‫חשש מ "תופעות לוואי" (‪ )side-effects‬של פרוצדורות‪.‬‬
‫–‪–4‬‬
‫מגבלות של מהדרים‬
‫נדרשים לעמוד במגבלות חמורות‪:‬‬
‫‪‬‬
‫‪‬‬
‫אסור להם לשנות את הסמנטיקה של התוכנית תחת כל תנאי‪.‬‬
‫דבר זה מונע מהם לבצע שינויים שהיו מתגלים כלא נכונים רק במקרים‬
‫שלעולם לא קורים בפועל‪.‬‬
‫התנהגות של התוכנית שהיא מובנת מאליה למתכנת‪ ,‬יכולה להיות‬
‫מוסתרת מפני המהדר בגלל סגנון תכנות‬
‫‪‬‬
‫למשל‪ :‬טווח ערכים במערך בפועל קטן יותר מאשר הטווח המוכרז‪.‬‬
‫–‪–5‬‬
‫מגבלות של מהדרים‬
‫רוב הניתוח המבוצע על ידי המהדר נעשה בתוך הפרוצדורות ולא‬
‫ביניהן‪.‬‬
‫‪‬‬
‫ניתוח של כלל התוכנית הוא לא מעשי בגלל אילוצי זמן‪.‬‬
‫מבוסס על מידע סטטי‬
‫‪‬‬
‫לא יכול לזהות קלט שיתקבל בזמן ריצה‪ ,‬וכו'‪.‬‬
‫חייב להיות שמרני!‬
‫–‪–6‬‬
‫אופטימיזציות שאינן תלויות מכונה‬
‫הזזת קוד )‪(code motion‬‬
‫‪‬‬
‫הקטן את מספר הפעמים שקוד מורץ‬
‫‪ ‬בתנאי שזה לא ישנה את התוצאה‬
‫‪ ‬במיוחד כשמדובר בהוצאת קוד מלולאות‬
‫{ )‪0; i < n; i++‬‬
‫;‪= n*i‬‬
‫)‪= 0; j < n; j++‬‬
‫;]‪+ j] = b[j‬‬
‫= ‪for (i‬‬
‫‪int ni‬‬
‫‪for (j‬‬
‫‪a[ni‬‬
‫}‬
‫)‪for (i = 0; i < n; i++‬‬
‫)‪for (j = 0; j < n; j++‬‬
‫;]‪a[n*i + j] = b[j‬‬
‫–‪–7‬‬
‫הזזת קוד על ידי המהדר‬
.‫בדר"כ מהדרים עושים עבודה טובה בטיפול במערכים ולולאות פשוטות‬

Code Generated by gcc
for (i =
int ni
int *p
for (j
*p++
}
for (i = 0; i < n; i++)
for (j = 0; j < n; j++)
a[n*i + j] = b[j];
multiply R1,R2
move 8(R3),R4
leal (R4,R2,4),R5
.L40:
move 12(R3),R4
move (R4,R6,4),R2
move R2,(R5)
add $4,R5
increment R6
compare R2 R6
jl .L40
–8–
0; i < n; i++) {
= n*i;
= a+ni;
= 0; j < n; j++)
= b[j];
#
#
#
#
i*n
a
p = a+i*n (scaled by 4)
Inner Loop
#
#
#
#
#
b
b+j (scaled by 4)
*p = b[j]
p++ (scaled by 4)
j++
# loop if j<n
Code Motion ‫דוגמה נוספת ל‬
Procedure to Convert String to Lower Case
void lower(char *s)
{
int i;
for (i = 0; i < strlen(s); i++)
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] -= ('A' - 'a');
}
.‫ המהדר לא יצליח לפתור‬,‫ כפי שנראה‬,‫את המקרה הזה‬
...‫האחריות – על המתכנת‬
–9–
Lower Case Conversion ‫ביצועים של‬
.‫זמן ריבועי באורך המחרוזת‬

lower1
1000
CPU Seconds
100
10
1
0.1
0.01
0.001
0.0001
256
512
1024
2048
4096
8192
16384
String Length
– 10 –
32768
65536
131072
262144
...goto ‫נתרגם לתוכנית‬
void lower(char *s)
{
int i = 0;
if (i >= strlen(s))
goto done;
loop:
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] -= ('A' - 'a');
i++;
if (i < strlen(s))
goto loop;
done:
}
– 11 –
‫ מבוצע בכל איטרציה‬strlen
'\0' ‫ מחפש את‬:‫ ליניארי באורך המחרוזת‬strlen

‫זמן ריבועי‬


‫שיפור הביצועים‬
void lower(char *s)
{
int i;
int len = strlen(s);
for (i = 0; i < len; i++)
if (s[i] >= 'A' && s[i] <= 'Z')
s[i] -= ('A' - 'a');
}
– 12 –
‫ מחוץ ללולאה‬strlen ‫הוצא קריאה ל‬

.‫שהרי התוצאה אינה משתנה בין האיטרציות‬
code motion ‫סוג של‬


‫זמני ריצה‬
‫זמן ריצה ריבועי לעומת ליניארי‬

CPU Seconds
1000
100
10
1
0.1
0.01
0.001
0.0001
0.00001
0.000001
256
512
1024
2048
4096
8192
16384
32768
String Length
lower1
– 13 –
lower2
65536
131072 262144
‫החלפת אופרטורים 'יקרים'‬
‫‪‬‬
‫‪Shift, add instead of multiply or divide‬‬
‫>‪--‬‬
‫‪x << 4‬‬
‫‪ ‬ההשפעה תלויות מכונה‪ :‬בכמה עולה כפל וחילוק‬
‫‪ ‬בפנטיום ‪ II‬כפל של מספר שלם צורך רק ‪ .CPU cycles 4‬חילוק – הרבה‬
‫יותר‪.‬‬
‫‪‬‬
‫‪16*x‬‬
‫זהה סדרה של הכפלות‪:‬‬
‫;‪int ni = 0‬‬
‫{ )‪for (i = 0; i < n; i++‬‬
‫)‪for (j = 0; j < n; j++‬‬
‫;]‪a[ni + j] = b[j‬‬
‫;‪ni += n‬‬
‫}‬
‫)‪for (i = 0; i < n; i++‬‬
‫)‪for (j = 0; j < n; j++‬‬
‫;]‪a[n*i + j] = b[j‬‬
‫קודם השתמשנו בכפל‬
‫)‪(ni = n *i‬‬
‫– ‪– 14‬‬
‫שימוש ברגיסטרים‬
‫‪‬‬
‫‪‬‬
‫קריאה וכתיבה לרגיסטרים מהירה בהרבה מקריאה‪/‬כתיבה לזיכרון‪.‬‬
‫המהדר לא יכול לקבוע איזה משתנים כדאי לשמור ברגיסטרים‪.‬‬
‫‪ ‬נותן קדימות למשתנים זמניים‬
‫‪ ‬נותן קדימות למשתנים המוגדרים על ידי המשתמש בעזרת המילה ‪register‬‬
‫– ‪– 15‬‬
‫צרוף ביטויים משותפים‬
.‫בדר"כ מהדרים לא מנצלים ידע אריתמטי‬
/* Sum neighbors of i,j */
up =
val[(i-1)*n + j];
down = val[(i+1)*n + j];
left = val[i*n
+ j-1];
right = val[i*n
+ j+1];
sum = up + down + left + right;
3 multiplications: i*n, (i–1)*n, (i+1)*n
leal -1(R1),R2
multiply R4,R2
leal 1(R1),R3
multiply R4,R3
multiply R4,R1
– 16 –
#
#
#
#
#
i-1
(i-1)*n
i+1
(i+1)*n
i*n
int inj = i*n +
up =
val[inj
down = val[inj
left = val[inj
right = val[inj
sum = up + down
j;
- n];
+ n];
- 1];
+ 1];
+ left + right;
1 multiplication: i*n
:‫כלים חשובים‬
:‫מדידה‬
‫חשב זמן ריצה הנצרך על ידי קטעי קוד מסויימים‬

‫ לרב המעבדים יש מוני שעון‬
...‫ לא קל להוציא מהם מידע שימושי‬
profiler: ‫השימוש ב‬

'‫ וכו‬,‫ מספר הקריאות לכל פונקציה‬
gcc –O2 –pg prog. –o prog
./prog
Executes in normal fashion,
but also generates file
gmon.out
gprof prog
Generates profile information
based on gmon.out
– 17 –
visual studio 6.0 :
Project/settings/link: enable
profiling
‫ הדר‬Build profile -
‫דוגמה ל ‪profiling‬‬
‫המטרה‬
‫‪‬‬
‫‪‬‬
‫חשב תדירות של מלים במסמך טקסט‬
‫הצג רשימה ממוינת מהמילה התדירה ביותר לנדירה ביותר‬
‫צעדים‪:‬‬
‫‪‬‬
‫‪‬‬
‫הפוך ל ‪lowercase‬‬
‫הפעל פונקציית גִּבּוב (‪.)hash function‬‬
‫‪ ‬נתחיל עם סכום ערכי ‪ ASCII‬של המחרוזת‪.‬‬
‫‪‬‬
‫קרא מלים והכנס לטבלת גבוב‪.‬‬
‫‪ ‬פונקציה רקורסיבית שמכניסה מלים חדשות בסוף‪.‬‬
‫‪ ‬עדכן מונה עבור כל מילה‬
‫‪‬‬
‫מיין תוצאות‬
‫‪ ‬נפעיל אלגוריתם בשם ‪( .insertion-sort‬אלגוריתם זה יוצר רשימה חדשה‪,‬‬
‫כל פעם מוסיף אליה איברים מהרשימה המקורית במקום הנכון)‪.‬‬
‫– ‪– 18‬‬
profiling ‫דוגמה ל‬
:‫הקלט‬
Shakespeare’s
most frequent words
29,801
the
27,529
and
21,029
I
20,957
to
18,514
of
15,370
a
14,010
you
12,936
my
11,722
in
11,519
that
– 19 –
Collected works of Shakespeare
946,596 total words, 26,596 unique
‫ שניות‬9.2 :‫מימוש ראשוני‬



‫ועכשיו‪Profiling :‬‬
‫הוסף ל ‪ executable‬פונקציות למדידת זמן‬
‫‪‬‬
‫‪‬‬
‫מחשב בקירוב זמן בתוך כל פונקציה‪.‬‬
‫שיטת החישוב‬
‫‪ ‬כל זמן מה (לערך ‪ )10 ms‬שולח פסיקה‬
‫‪ ‬קובע איזו פונקציה כרגע מבוצעת‬
‫‪ ‬מעדכן את מונה הזמן ב ‪10ms‬‬
‫‪‬‬
‫גם מעדכן מונה של מספר הקריאות לפונקציה‬
‫– ‪– 20‬‬
‫תוצאות ה‪Profiling -‬‬
‫‪name‬‬
‫‪sort_words‬‬
‫‪lower1‬‬
‫‪find_ele_rec‬‬
‫‪h_add‬‬
‫‪total‬‬
‫‪ms/call‬‬
‫‪8210.00‬‬
‫‪0.00‬‬
‫‪0.00‬‬
‫‪0.00‬‬
‫‪self‬‬
‫‪ms/call‬‬
‫‪8210.00‬‬
‫‪0.00‬‬
‫‪0.00‬‬
‫‪0.00‬‬
‫‪calls‬‬
‫‪1‬‬
‫‪946596‬‬
‫‪946596‬‬
‫‪946596‬‬
‫‪self‬‬
‫‪seconds‬‬
‫‪8.21‬‬
‫‪0.55‬‬
‫‪0.45‬‬
‫‪0.12‬‬
‫‪%‬‬
‫‪cumulative‬‬
‫‪time‬‬
‫‪seconds‬‬
‫‪86.60‬‬
‫‪8.21‬‬
‫‪5.80‬‬
‫‪8.76‬‬
‫‪4.75‬‬
‫‪9.21‬‬
‫‪1.27‬‬
‫‪9.33‬‬
‫סטטיסטיקת קריאות‬
‫‪‬‬
‫מספר קריאות וסה"כ זמן בכל פונקציה‬
‫הבעיה בביצועים‪:‬‬
‫‪‬‬
‫‪‬‬
‫המיון לוקח זמן רב מדי‪ :‬אלג' לא יעיל‪.‬‬
‫קריאה בודדת לוקחת ‪ 87%‬מזמן הריצה‪.‬‬
‫– ‪– 21‬‬
‫אופטימיזציות‬
10
9
CPU Secs.
8
7
Rest
6
Hash
5
Lower
4
List
3
Sort
2
1
0
Initial
Quicksort
Iter First
Iter Last
Big Table
Better Hash
Linear Lower
‫ החלף פרוצדורת מיון‬:‫צעד ראשון‬
qsort ‫נשתמש בפונקציית הספרייה‬
– 22 –


‫אופטימיזציות נוספות‬
‫‪2‬‬
‫‪1.8‬‬
‫‪1.6‬‬
‫‪Hash‬‬
‫‪1.2‬‬
‫‪1‬‬
‫‪Lower‬‬
‫‪List‬‬
‫‪0.8‬‬
‫‪Sort‬‬
‫‪0.6‬‬
‫‪CPU Secs.‬‬
‫‪Rest‬‬
‫‪1.4‬‬
‫‪0.4‬‬
‫‪0.2‬‬
‫‪0‬‬
‫‪Linear Lower‬‬
‫‪‬‬
‫‪Better Hash‬‬
‫‪Big Table‬‬
‫‪Iter Last‬‬
‫‪Iter First‬‬
‫‪Quicksort‬‬
‫‪Initial‬‬
‫‪ :Iter first‬במקום פונקציה רקורסיבית – לולאה‪ .‬מוסיף איבר חדש לראש‬
‫הרשימה‪.‬‬
‫‪ ‬מאט את הקוד‬
‫‪‬‬
‫‪ :Iter last‬כנ"ל‪ ,‬אבל הוסף איבר חדש לסוף הרשימה‪.‬‬
‫‪ ‬מלים נפוצות יותר יהיו קרוב לתחילת הרשימה‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫‪ :Big table‬טבלת גיבוב גדולה יותר‬
‫‪ :Better hash‬פונקצית גיבוב טובה יותר‬
‫‪ :Linear lower‬הוצאת ‪ strlen‬מהלולאה‬
‫– ‪– 23‬‬
‫מספר מלים על ‪Profiling‬‬
‫יתרונות‬
‫‪‬‬
‫‪‬‬
‫עוזר לזהות צווארי בקבוק בביצועים‬
‫שימושי בעיקר כשמדובר במערכת מורכבת עם רכיבים רבים‪.‬‬
‫מגבלות‬
‫‪‬‬
‫‪‬‬
‫תלוי קלט‬
‫למשל‪ ,‬הטכניקה של ‪ linear lower‬לא הראה את כוחו בגלל שרב המלים‬
‫הן קצרות‪.‬‬
‫‪ ‬ייתכן שלא נזהה חוסר יעילות כגון זמן ריצה ריבועי במקום ליניארי‪.‬‬
‫‪‬‬
‫מנגנון חישוב הזמן לא מאוד מדויק‬
‫‪ ‬לא עובד במקרים של תוכניות הרצות פחות מ ‪ 3‬שניות‪.‬‬
‫– ‪– 24‬‬
‫מדדי זמן‬
‫זמן אבסולוטי‬
10–9 sec :‫שניות‬-‫בדר"כ ננו‬
‫סדר גודל מציאותי‬


Clock Cycles
‫טווח טיפוסי‬
100 MHz 
108 cycles per second »
Clock period = 10ns »
2 GHz 
2 X 109 cycles per second »
Clock period = 0.5ns »
– 25 –

Cycles Per Element (CPE)


‫ רשימת נתונים‬/ ‫ דרך נוחה למדידת ביצועים במקרה של וקטור‬
Length = n
‫ שתי תוכניות עם הפרש‬vsum1, vsum2
T = CPE*n + Overhead
.‫ליניארי בביצועים‬
1000
900
800
vsum1
Slope = 4.0
700
Cycles
600
500
vsum2
Slope = 3.5
400
300
200
100
0
0
50
100
Elements
– 26 –
150
200
‫ חיבור איברי וקטור‬:‫דוגמה‬
length
data
0 1 2
length–1
  
Procedures
vec_ptr new_vec(int len)
 Create vector of specified length
int get_vec_element(vec_ptr v, int index, int *dest)
 Retrieve vector element, store at *dest
 Return 0 if out of bounds, 1 if successful
int *get_vec_start(vec_ptr v)
 Return pointer to start of vector data

Similar to array implementations in Pascal, ML, Java
 E.g., always do bounds checking
– 27 –
‫המשך דוגמה‬
void combine1(vec_ptr v, int *dest)
{
int i;
*dest = 0;
for (i = 0; i < vec_length(v); i++) {
int val;
get_vec_element(v, i, &val);
*dest += val;
}
}
‫מטרה‬
‫חשב סכום איברי הוקטור‬
.dest ‫שמור תוצאה ב‬


Pentium III Performance: Clock Cycles / Element

– 28 –
42.06 (Compiled -g) 31.25 (Compiled -O2)
)2 ‫ אופטימיזציות ברמה‬:-O2 ,debug information :-g(

...‫לולאות‬
void combine1-goto(vec_ptr v, int *dest)
{
int i = 0;
int val;
*dest = 0;
if (i >= vec_length(v))
goto done;
1 iteration
loop:
get_vec_element(v, i, &val);
*dest += val;
i++;
if (i < vec_length(v))
goto loop
done:
}
:‫חוסר יעילות‬
‫ נקראת בכל איטרציה‬vec_length ‫השגרה‬
.‫למרות שהתוצאה זהה‬
– 29 –


‫ אל מחוץ ללולאה‬vec_length ‫הוצא את‬
void combine2(vec_ptr v, int *dest)
{
int i;
int length = vec_length(v);
*dest = 0;
for (i = 0; i < length; i++) {
int val;
get_vec_element(v, i, &val);
*dest += val;
}
}
‫אופטימיזציה‬
.‫ מהלולאה הפנימית‬vec_length ‫הוצא את‬
.‫ הערך לא משתנה‬
Code motion 

– 30 –
CPE: from 31.25 down to 20.66 (Compiled -O2)
.‫ אבל עם תקורה משמעותית‬,‫ דורש זמן קבוע‬vec_length 

‫חוסמי אופטימיזציות‪ :‬פונקציות‬
‫מדוע‪ ,‬בעצם‪ ,‬לא יכול המהדר להוציא את ‪ vec_len‬או את ‪strlen‬‬
‫מחוץ ללולאה הפנימית ?‬
‫‪‬‬
‫‪‬‬
‫לפונקציות יכול להיות ‪side effects‬‬
‫ייתכן שהפונקציה משנה את המצב הגלובלי‬
‫‪ ‬ייתכן שהיא מחזירה ערכים שונים עבור קריאה עם אותם ארגומנטים‪.‬‬
‫‪ ‬ייתכן של ‪ strlen -‬יש אינטראקציה עם ‪.lower‬‬
‫מדוע בעצם המהדר לא מסתכל על הקוד של ‪? vec_len, strlen‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫אם ההידור הוא לא סטטי‪ ,‬המקשר )‪ (linker‬עלול לקשר עם גירסה אחרת של‬
‫אותה פונקציה‪.‬‬
‫אופטימיזציות המערבות מספר פרוצדורות אינה מעשית בדר"כ בגלל זמן ריצה‪.‬‬
‫המהדר מתייחס לפונקציות כ‪' -‬קופסה שחורה'‬
‫– ‪– 31‬‬
‫עוד אופטימיזציות‪...‬‬
‫)‪void combine3(vec_ptr v, int *dest‬‬
‫{‬
‫;‪int i‬‬
‫;)‪int length = vec_length(v‬‬
‫;)‪int *data = get_vec_start(v‬‬
‫;‪*dest = 0‬‬
‫{ )‪for (i = 0; i < length; i++‬‬
‫;]‪*dest += data[i‬‬
‫}‬
‫אופטימיזציות‬
‫‪‬‬
‫הימנע‪/‬י מקריאה לפונקציה כדי להביא את האלמנט הבא‬
‫‪ ‬מצא מצביע לתחילת הרשימה לפני הלולאה‬
‫‪ ‬בתוך הלולאה התייחס למצביע‪.‬‬
‫‪( ‬פחות מודולרי ומתאים ל ‪)abstract data types‬‬
‫‪CPE: 20.66  6.00‬‬
‫‪‬‬
‫‪ ‬קריאות לפונקציות הן יקרות‬
‫‪ ‬הורדנו גם את זמן בדיקת האינדקס (בדיקה שהוא בגבולות המערך)‪.‬‬
‫– ‪– 32‬‬
‫הקטן גישות לזיכרון‬
‫)‪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‬‬
‫}‬
‫עוד אופטימיזציה‪...‬‬
‫‪‬‬
‫אין צורך לשמור את התוצאה ב ‪ dest‬עד הסיום‬
‫המשתנה הלוקאלי ‪ sum‬נשמר ברגיסטר‬
‫‪‬‬
‫חוסך קריאה אחת וכתיבה אחת לזיכרון בכל איטרציה‪.‬‬
‫)‪CPE: 6.00  2.00 (we started from 31.25‬‬
‫גישה לזיכרון צורכת זמן רב‪.‬‬
‫‪‬‬
‫‪‬‬
‫‪‬‬
‫– ‪– 33‬‬
‫חוסמי אופטימיזציות‪Memory Aliasing :‬‬
‫‪ ‬מדוע המהדר לא הפך את ‪ combine3‬ל ‪ combine4‬בעצמו?‬
‫‪‬התשובה‪ :‬חשש מ ‪ִ ( Aliasing‬כנּוי)‬
‫‪‬‬
‫שתי התייחסויות שונות לזיכרון מתייחסות לאותו מיקום‪.‬‬
‫]‪v: [2, 3, 6‬‬
‫)‪ combine3(v, get_vec_start(v)+2‬‬
‫>‪--‬‬
‫האיבר האחרון בווקטור עכשיו יאחסן את התוצאה‪.‬‬
‫]‪[2,3,0] [2,3,2] [2,3,5] [2,3,10‬‬
‫>‪--‬‬
‫)‪combine4(v, get_vec_start(v)+2‬‬
‫‪‬‬
‫התוצאה שונה‪.‬‬
‫]‪[2,3,6] [2,3,6] [2,3,6] [2,3,11‬‬
‫מסקנה‪ combine3 :‬ו ‪ combine4‬הן לא בהכרח זהות בנוכחות כינויים‪.‬‬
‫– ‪– 34‬‬
‫חוסמי אופטימיזציות‪Memory Aliasing :‬‬
‫נשים לב‪:‬‬
‫‪ ‬ב ‪ aliasing , C‬נפוץ‪.‬‬
‫‪ ‬בגלל שניתן לבצע ‪( address arithmetic‬כלומר‪ ,‬חישוב מפורש של כתובת)‬
‫‪ ‬יש גישה ישירה למבנה המאחסן את הנתונים‪.‬‬
‫‪‬‬
‫כדאי להשתמש במשתנים לוקאליים‬
‫‪ ‬בעיקר בתוך לולאות‬
‫‪ ‬כך המהדר יודע שהוא לא צריך לבדוק ‪.aliasing‬‬
‫– ‪– 35‬‬
‫סיכום‪ :‬אופטימיזציות שאינן תלויות מכונה‪.‬‬
‫הזזת קוד (‪)Code Motion‬‬
‫‪‬‬
‫‪‬‬
‫מהדרים יודעים לעשות זאת במקרים מסוימים‪ :‬מערכים פשוטים ולולאות‬
‫פשוטות‪.‬‬
‫לא יודעים לעשות זאת בנוכחות של 'כינויים' וקריאות לפונקציות‪.‬‬
‫החלפת אופרטורים יקרים‬
‫‪‬‬
‫‪Shift, Add, instead of Multiply or Divide‬‬
‫‪ ‬בדר"כ מהדרים מצליחים לעשות זאת לבד‪ ,‬אבל לא תמיד‪.‬‬
‫‪ ‬בכל זאת‪ ,‬תלוי מכונה‬
‫‪‬‬
‫כשאפשר‪ ,‬שמור מידע ברגיסטרים במקום בזיכרון‬
‫‪ ‬למהדרים קשה לזהות הזדמנויות‪ ,‬בגלל חשש לשימוש בכינויים‪.‬‬
‫איחוד ביטויים משותפים‪.‬‬
‫‪‬‬
‫למהדרים יש יכולת אלגבראית מוגבלת‪.‬‬
‫– ‪– 36‬‬