אני מחפש נתב חדש במקום הצ'יקמוק שיש לי כרגע.
זה מה שאני רוצה ממנו:
1. תמיכה ברשת את'רנט ג'יגהביט
2. תמיכה בכבלים ובADSL בארצנו.
3. תמיכה בתקן אלחוטי 802.11n
יש המלצות?
MySQL and the number of the beast
מי שמצליח להצדיק את התוצאה הזו מMySQL יזכה בחופשה בקאריבים:
[code lang="sql"]
create table t(a varchar(10));
insert into t(a) values('aaa'),('bbb'),('ccc'),('ddd');
select * from t where a in (666,'a');
+——+
| a |
+——+
| aaa |
| bbb |
| ccc |
| ddd |
+——+
[/code]
C יותר מהירה מJava
כולם יודעים שC יותר מהירה מג'אווה, נכון?
פרוייקטים רציניים של גריסת מספרים (Number crunching) כמו עיבוד תמונה בזמן אמת, זיהוי קול, רינדור, דחיסה, קידוד ווידאו וכו בדרך כלל נכתבים בC (או C++).
בהינתן שתי פיסות קוד שעושות בדיוק את אותו דבר, מעניין לראות את במה מתבטא היתרון של C על ג'אווה.
למה אפשר לצפות ליתרון?
כי ג'אווה רצה מעל JVM, והJVM מוסיף תקורה, ברור שC תרוץ יותר מהר כי היא רצה ישר על הCPU ולא דרך הJVM.
הנה שתי פיסות קוד, אחת בג'אווה ואחת בC. שתי התוכניות מאתחלות שתי מטריצות גדולות ומכפילות אותן אחת בשניה, הקוד בהחלט לא יעיל במיוחד ברמת האלגוריתם, אבל הוא זהה מבחינה מימושית.
הנה הקוד:
תוכנית C:
[code lang="c"]
#include
#include
int main(int argc, char **argv)
{
int i,j,k;
int N = 2500;
printf("N = %d\n", N);
double *A = malloc(N*N*sizeof(double));
double *B = malloc(N*N*sizeof(double));
double *C = malloc(N*N*sizeof(double));
double *bj = malloc(N*sizeof(double));
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
{
int n = i*N+j;
A[n] = i * j;
}
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
B[i*N+j] = i * j * j;
// order 7: jik optimized ala JAMA
for (j = 0; j < N; j++) {
for (k = 0; k < N; k++)
bj[k] = B[k*N+j];
for (i = 0; i < N; i++) {
double s = 0;
for (k = 0; k < N; k++) {
s += A[i*N+k] * bj[k];
}
C[i*N+j] = s;
}
}
printf("done\n");
return 0;
}
[/code]
תוכנית ג'אווה:
[code lang="java"]
public class Matrix
{
public static void main(String[] args)
{
int i,j,k;
int N = 2500;
System.err.println("N = " + N);
double A[] = new double[N*N];
double B[] = new double[N*N];
double C[] = new double[N*N];
double bj[] = new double[N];
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
{
int n = i*N+j;
A[n] = i * j;
}
for (i = 0; i < N; i++)
for (j = 0; j < N; j++)
B[i*N+j] = i * j * j;
// order 7: jik optimized ala JAMA
for (j = 0; j < N; j++) {
for (k = 0; k < N; k++)
bj[k] = B[k*N+j];
for (i = 0; i < N; i++) {
double s = 0;
for (k = 0; k < N; k++) {
s += A[i*N+k] * bj[k];
}
C[i*N+j] = s;
}
}
System.err.println("done");
}
}
[/code]
מי לוקח התערבות של בכמה C עוקפת את ג'אווה בזמן הריצה של זה?
נקמפל ונבדוק:
[code]
$javac Matrix.java
$gcc Matrix.c -o matrix
$ date;java Matrix;date;./matrix;date
Thu Jun 26 08:42:10 IDT 2008
N = 2500
done
Thu Jun 26 08:42:54 IDT 2008
N = 2500
done
Thu Jun 26 08:44:31 IDT 2008
[/code]
לתוכנית בג'אווה לקח לקח 44 שניות ולתוכנית בC לקח 107 שניות.
מש"ל.
אה, רגע. רצינו להראות שC יותר מהירה!
טוב, מסתבר שלא כדאי לקחת דברים כמובנים מאליהם, גם אם כולם יודעים שהם נכונים.
אם אתם חושבים שרימיתי, תריצו בעצמכם. בדקתי על שני מחשבים, אחד עם שתי ליבות של 3GHZ, ואחד עם ארבע ליבות של 2.4GHZ (כמובן שהראשון הוביל בכמה אחוזים טובים, אבל היחס נשמר).
השתמשתי בJava 1.6.06.
לדעתי התופעה הזו נובעת מההתקדמות המדהימה של סביבת הריצה של ג'אווה בתחום הHotspot.
Hotspot היא טכנולוגיה שמקמפלת חלקים "חמים" בתוכנית בזמן, אבל בזמן ריצה. מכיוון שזמינות לHotspot סטטיסטיקות בזמן הריצה הממשי של התוכנית היא יכולה לשנות את הקוד ככה שירוץ בצורה אופטימלית לאור התנהגות של התוכנית ולא כנסיון מלומד לנחש מה יהיה יותר מהר מהתבוננות ושינוי הקוד, מה שעושה קומפיילר סטאטי.
עדכון:
קימפלתי את התוכנית C עם אופטימיזציה מקסימלית והתוצאה שלה השתפרה פלאים:
[code]
gcc -O3 Matrix.c
$ date;./matrix;date
Thu Jun 26 11:26:00 IDT 2008
N = 2500
done
Thu Jun 26 11:26:29 IDT 2008
[/code]
הפעם התוצאה של C היא 29 שניות.
טוב משמעותית מקודם, וגם יותר מהיר בכ30% מג'אווה.
עדכון 2:
שמתי לב שקוד שקומפל עם javac איטי מקוד שקומפל בeclipse. נחשתי שeclipse מקמפל עם jikes (אני לא בטוח בזה).
ניסיתי עם jikes והתוצאה השתוותה, תיקו 29 שניות.
[code]
$ javac Matrix.java ; time java Matrix
N = 2500
done
real 0m42.854s
user 0m55.335s
sys 0m26.214s
$ jikes --bootclasspath /usr/lib/jvm/java-6-sun-1.6.0.06/jre/lib/rt.jar Matrix.java ; time java Matrix
N = 2500
done
real 0m29.463s
user 0m29.366s
sys 0m0.108s
[/code]
אגב, זו תוצאה מדהימה שכדאי שכל מפתח ג'אווה יכיר.
מי רוצה לשפר את התוצאות עוד?
ג'ורג' קרלין מת בגיל 71
ג'ורג' קרלין, קומיקאי ענק – מת אתמול בגיל 71.
ראיתי אותו בהופעה בלאס ווגאס ב2004, תותח.
למי שלא מכיר, הנה דוגמית של ג'ורג' קרלין יורד על האבטחה בשדות תעופה.
יש עוד הרבה באתה-צינור.
שעון עולם
Day of release
בצירוף מקרים קוסמי שחררתי היום גרסאות חדשות לשלושה פרוייקטים בלתי תלויים:
FireStats 1.5.9-RC3:
FireStats 1.5 מתייצב, ורוב הסיכויים שהגרסא הבאה תהיה הגרסא היציבה של הענף הזה.
רשימת השינויים נמצאת כאן.
WPMU Plugin Commander 1.1.0
WPMU Plugin Commander הוא תוסף ניהוי תוספים לWPMU, שמוסיף כמה תכונות מאוד נדרשות לWPMU (אפשרות לבחור איזה תוספים משמשים יכולים להפעיל ולכבות, אפשרות להפעיל אוטומטית תוספים עבור בלוגים חדשים ועוד)
אתמול בלילה קיבלתי תרומת קוד שמוסיפה תמיכה בריבוי שפות לPlugin Commander, וכן שינויי עיצוב שגורמים לא להיות הרבה פחות מכוער.
בנוסף, התורמת של הקוד תרמה גם תרגום לרוסית של התוסף.
Antenna 1.1.0-beta
קיבלתי תרומת קוד גדולה מצוות MTJ, הקוד מממש את הPreprocessor שכתבתי בANTLR 3.0, מה שיאפשר הכנסה של הPreprocesror לתוך MTJ.
MTJ הוא פרוייקט שמטרתו להכניס תמיכה בפיתוח קוד J2ME בEclipse. הפרוייקט יתבסס על EclipseME, שהוא הסטנדרט דה-פקטו לפיתוח J2ME עם Eclipse. הPreprocessor שלי כבר נמצא בתוך EclipseME די הרבה זמן, אבל היתה בעיה עם הרשיון של ANTR 2.7.7 שבו השתמשתי כדי לפתח אותו, ו Eclipse legal לא אישרו את הכנסת הקוד שלי לMTJ מהסיבה הזו.
הצוות של MTJ בחר לבצע Porting של הPreprocessor כך שיעבוד עם ANTLR 3.0 כי הרשיון שלו תואם את זה של Eclipse.
לפני כמה ימים הצוות קיבל אישור מEclipse legal לתת לי את הקוד שהם יצרו כדי שאני אקלוט אותו לתוך Antenna.
הקוד היה איכותי, ועבר את כל בדיקות היחידה שהגדרתי כבר, מה שנתן לי ביטחון גבוה שהוא עובד כמו שצריך. אחרי כמה שעות עבודה, הקוד הוטמע והיום שחררתי גרסא חדשה של Antenna שמשתמשת בPreprocessor החדש כברירת מחדל.
גלויה מסין
יוצא לי לעזור לאנשים בפרוייקטי קוד פתוח שאני מעורב בהם מפעם לפעם, תמיד אני מקבל תודה, אבל אף פעם לא קיבלתי גלויה.
עזרתי לוויליאם בבעיה שהיתה לו בשימוש בAntenna, פרוייקט קוד פתוח שעוזר בתהליך הבניה של ישומי ג'אווה לסלולריים, וקיבלתי גלויה והזמנה לשנחאי :).
The root of all evil
איכות תוכנה נמדדת בכמה פרמטים:
נכונות (correctness): האם התוכנה עושה מה כל מה שהיא צריכה לעשות, ובצורה נכונה?
קלות תחזוקה (maintainability) : כמה קל למצוא ולטפל בבאגים? כמה קל להרחיב את המערכת?
ביצועים (performance) : האם התוכנה עובדת מהר מספיק?
המדדים האלו נוטים לבוא אחד על חשבון השני.
קוד נכון וקל לתחזוקה הוא בדרך כלל פחות יעיל.
קוד נכון ויעיל הוא בדרך כלל קשה לתחזוקה.
קוד יעיל וקל לתחזוקה.. אין דבר כזה :).
שיפור ביצועים בתוכנה הוא אחד הנושאים שכל מפתח מגיע אליו בשלב מסויים. בדרך כלל מוקדם בהרבה ממה שצריך.
הבעיה עם שיפור ביצועים היא שהוא מעלה את מורכבות הקוד, מה שפוגע אוטומטית בקלות התחזוקה, ומעלה את הקושי בשמירה על נכונות.
חכמים ממני כבר אמרו שאופטימיזציה מוקדמת מדי היא שורש כל רע :
כשמשפרים ביצועים מוקדם מדי, קשה להוסיף אחר כך תכונות נוספות לאפליקציה, כי המורכבות של הקוד עולה.
הבעיה היא שגם שכבר מגיע הזמן לשפר ביצועים, רוב המפתחים מבזבזים את הזמן שלהם בשיפורים חסרי ערך שרק מעלים את מורכבות האפליקציה, תוך רווח שולי מאוד בביצועים, לפעמים רווח כל כך שולי שהוא בלתי מורגש.
כשבאים לשפר ביצועים, חשוב להשקיע את הזמן בשיפורים הנכונים.
הדרך הנכונה היא לא להסתכל על הקוד, למצוא פיסת קוד לא יעילה, לשפר אותה ולהכריז שהאפליקציה עובדת יותר מהר.
הדרך הנכונה היא קודם כל לאתר מועמדים לאוטימיזציה, לא לפי איפה שאפשר לעשות אופטימיזציה אלא לפי איפה שצריך והדרך לשם היא למדוד את ביצועי האפליקציה בטיפול במשימות בעייתיות מבחינת ביצועים (בין אם זה יהיה זמן איתחול, פעולה מסויימת בממשק המשתמש שמרגישה איטית או כל דבר אחר).
כשמודדים, חשוב להבין היטב באיזה חלק של הקוד התוכנית מבלה את רוב הזמן, זאת על ידי מדידות של תתי משימות בתוך אותה משימה בעייתית.
ברגע שהבנו את התנהגות האפליקציה ואיתרנו את הגורם או הגורמים לאיטיות, הגיע הזמן לנסות לשפר את הביצועיים.
בשלב הזה רוב המפתחים פשוט ירוצו וישכתבו חלקים מהקוד כדי לגרום להם לרוץ יותר מהר, בדרך כלל תוך העלאת המורכבות של הקוד. הבעיה היא שגם אם אכן יש שיפור ביצועים – איך נחליט אם הוא מצדיק את השינויים שעשינו?
המשמעות של השינויים האלו, היא כאמור עליה במורכבות הקוד, מה שיקשה עלינו להוסיף תכונות עתידיות לקוד ויקשה עלינו לאתר ולתקן באגים.
אולי השיפור בפועל הוא 2% בלבד ולא מצדיק את המחיר הזה?
כדי להחליט צריך להשוות את הביצועים לפני ואחרי השינוי, ולהחליט אם השיפור מצדיק את השינוי בקוד.
הדרך לעשות את זה היא ליצור בדיקה מוגדרת היטב שניתן להריץ שוב ושוב ולקבל מדידות זמן כמעט זהות בין הרצות שונות, ואז לראות בכמה אנחנו מצליחים לשפר את זמן הריצה של הקוד.
כדאי שהבדיקות יריצו בעיקר קוד אמיתי של התוכנית, למפתחים יש נטיה ליצור בדיקות סינטטיות שמראות שיפורי ביצועיים אסטרונומיים בנקודות מסויימת, אבל בתמונה הכללי הרבה פעמים השיפורים הם הרבה פחות משמעותיים.
דוגמא פשוטה:
קוד אמיתי מהתוכנית
[code lang="java"]
// do stuff
doSomething();
// do more stuff
[/code]
בדיקה סינטטית:
[code lang="java"]
int start = time();
for (int i=0;i<1000000;i++)
{
doSomething();
}
int elapsed = time() - start;
print "average time is " + (elapsed / 1000000);
[/code]
צריך לשים שהבדיקה הסינטטית בודקת תזמון של פעולה אחת בזרימה של התוכנית, ויש עוד פעולות (do stuff וdo more stuff). ככה שגם אם נשפר את הביצועים של הבדיקה ב90% - אם הפעולות האחרות לוקחות בסך הכל שניה, וdoSomething לוקחת 10 מילישניות אז השיפור הוא זניח במקרה הזה.
בפוסט הבא בסדרה הזו אני אתאר דוגמא מעשית של אופטימיזציה של שאילתות MySQL
ניהול מלאי חומרה – Inventory
לפני כחודש שאלתי אתכם (קוראי הלא שימושיים בעליל) אם אתם מכירים פיתרון לניהול חומרה.
לא קיבלתי הצעות קונקרטיות.
אז חיפשתי שוב, ומצאתי את Inventory, שנותנת כמעט בדיוק את מה שרציתי.
Inventory כתובה בPHP, ומשוחררת ברשיון GPL.
היא גמישה מאוד, ומאפשרת להגדיר סוגי חומרה כאוות נפשכם, להגדיר ספקים, להגדיר רכיבי חומרה ספיצפיים (ולציין ממי הם ניקנו, מתי, מה אורך האחריות וכו'), ולהרכיב מהם מחשבים.
בנוסף היא תומכת גם בניהול תוכנות ורשיונות, אבל לי אישית זה פחות שימושי.
האפשרות שחסרה לי ביותר שם היא האפשרות להצמיד מסמכים (צילומים של קבלות ותעודות אחריות, צילומים של רכיבי חומרה וכו'), לכל רכיב.
הכל תלוי, מהתקרה
הגיע לי המסך, ובעזרתו האדיבה של יוקס הדג-שטן תליתי את המסך ואת המקרן במקום בו הם ישארו בשנה הקרובה לפחות.
המסך ברוחב של 2.44 ס"מ, ושוקל 11 ק"ג.
תליתו אותו מול החלון, שגם ככה אמור להיות סגור כשאני משתמש במסך, אז זה די מושלם.
כדי לתלות את המסך, קדחתי ארבע חורים בתקרה, שניים בכל צד, תקעתי לתוכם דיבלים של 8 מ"מ, ובעזרת הדג-שטן קיבעתי את המסך לתקרה.
זה לא היה פשוט, במיוחד לאור זה שאין פה שום מרווח טעות. צריך לקדוח בול במקום מהנסיון הראשון, ואי אפשר לעשות את זה בזמן שמחזיקים מעל הראש מוט ששוקל 11 קילו בלי תזוזה.
את המקרן כבר היה יותר קל לתלות. שוב, ארבע חורים, ארבע דיבלים של 8 מ"מ, קיבוע של החלק הקבוע של המתלה לקיר, הרמה של המקרן, קצת הברגה, ולבסוף כיוונוני זווית מקרן ותמונה.
את הכבל חשמל והכבל וידאו (DVI לHDMI) הצמדתי לקיר בעזרת אקדח דבק חם, כלי נחמד שמשתמש שמחמם צינור דבק לבן והופך אותו למעין סיליקון חצי שקוף עם דביקות די גבוהה.
אני חייב לציין שלתלות מסך של מקרן זה קריעת תחת, כל העסק לקח כמה שעות עבודה.
(התמונה לחיצה לגרסא ברזולוציה מלאה).