Liran Chen's Blog

.Net Internals, Development, Multithreading - and More!

Browse by Tags

All Tags » DEV (RSS)
Accurately Measuring GC Suspensions
כשבאים לנתח ביצועים של אפליקציית Managed, ורוצים להבין מה הם צווארי הבקבוק שעשויים להכשיל אותה בתרחישי עומס איתם היא אמורה להתמודד, אחד הפרמטרים החשובים שצריך לשים לב אליהם הוא מידת הזמן שהאפליקציה שלנו מבלה ב-GC. בשביל לדעת את זה, תמיד אפשר להריץ במקביל לאפליקציה את Perfmon ולקבל ניתוח כללי של התנהגות ה-GC באפליקציה שלנו, כשהמדד העיקרי שנסתכל עליו בדרך כלל הוא אחוז ה-Time in GC. התבוננת בנתונים האלה יכולים אמנם לתת לנו "מבט כללי" על השפעת ה-GC על ביצועי האפליקציה, אבל הוא פשוט לא מספיק...
The Case of Delayed ACKs and Nagle's Algorithm
למרות שההשלכות של השילוב בין Nagle's Algorithm ו-Delayed ACKs בעבודה עם TCP מתועדות היטב בספרות, עדיין מדובר בפרט שלא פעם קל לשכוח ממנו ולא לקחת אותו בחשבון כשצריך. מאחר ולסימפטומים הבעייתים של השילוב ביניהם יש נטייה לצוף רק תחת אוסף של תנאים מקדימים, יכול לעבור קצת זמן עד שמשייכים את את ה"תופעות המעניינות" לשימוש בשני האלגוריתמים הנ"ל. במקרה הזה, ככל שהסימפטומים שנוצרים עקב השילוב חריפים יותר, כך גם יותר קל להבין את המקור שלהם. אבל קודם כל, נבין מה הרעיון מאחורי כל אחד מהאלגוריתמים...
Behind .locals init
כפי שכולנו מכירים, #C דורשת שכל המשתנים הלוקאלים יאותחלו לפני השימוש בהם. עם זאת, למי שיצא להעזר ב-ildasm בשביל להציץ לתוך קוד ה-IL שהקומפיילר מייצר, בוודאי שם לב שמייד לאחר ההכרזה על שם הפונקציה, מתווספת שורה בסגנון הבא: .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 10 (0xa) .maxstack 1 .locals init ([0] int32 x) <--- localsinit flag IL_0000: ldc.i4.4 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: call void [mscorlib]System.Console::WriteLine(int32...
Don't Rely on Environment.ProcessorCount
אחת התורות הנסתרות בפיתוח מקבילי היא השאלה "בכמה ת'רדים צריך להשתמש כדי להגיע לניצול מירבי של החומרה העומדת לרשותנו?". יש שיגידו שמספר הת'רדים צריך להיות כמספר המעבדים, או כמספר המעבדים +1, או אולי בכלל פי 2 ממספר המעבדים. הסיבה שיש כל כך הרבה תשובות לשאלה, היא פשוט בגלל הסיבה שעבור כל תרחיש מסויים, תתאים תשובה אחרת (לכל אפליקציה יש אופי שונה, למשל האם היא מוגבלת על ידי ה-CPU או ה-IO?). אבל בכל אופן, תמיד הנוסחאות האלה מתבססות באיזשהיא צורה על מספר המעבדים הזמינים לנו (סך הכל, אנחנו...
Hot/Cold Data in Multithreaded Environments
בתקופה האחרונה שמתי לב שהנושא של False Sharing עולה לעתים די קרובות בבלוגים שמפרסמים פוסטים המסבירים במה בעצם מדובר ובאיך אפשר להמנע מהתופעה. כך שכמובן שהגיע הזמן שאתייחס בעצמי לנושא החשוב אך חמקמק הזה. אבל קודם כל, נסביר בקצרה במה בעצם מדובר ובאיפה טמונה הבעיה. אחד הנושאים הרגישים בפיתוח קוד מקבילי הוא הגישה לזיכרון המשותף למספר Thread'ים שונים. (כדי שנשאר ממוקדים בנושא הפוסט, אני אתעלם לרגע מבעיות העלולות להגרם כתוצאה מ-Instruction Reordering או אופטימיזציות אחרות שמבוצעות ברמת הקומפיילר או...
String.Format Isn't Suitable for Intensive Logging
אחד השיעורים הראשונים שכל מפתח דוט-נט לומד לשנן, הוא ש"בכל מקרה בו רוצים לחבר הרבה מחרוזות אחת לשניה, אסור בתכלית האיסור להשתמש באופרטור +, ובמקום זה להשתמש במחלקה StringBuilder". אז אחרי הדקלום המושלם הזה, אתה שואל "אוקיי, אבל למה בעצם?", והתשובה היא בדרך כלל "בגלל שמחרוזות הן Immutable, זה אומר ששימוש באופרטור+ יביא להקצאות מיותרות של מחרוזות שיהווה כל מיני 'שלבי ביניים' עד שנגיע למחרוזת הסופית". כפי שאני רואה את זה, הדקלום הזה יותר מטעה מאשר מה שהוא תורם....
A Small But Helpful Tip
כמה פעמים כבר יצא לכם שבזמן שאתם כותבים קוד, פתאום קופץ לכם על המסך הקומבו-בוקס המוכר של ה-Intellisense, או אולי איזה קטע מהתיעוד ה-XML'י של הפונקציה שאתם מנסים לקרוא לה. ואותו חלון בדיוק נופל על קטע קוד שאתם מנסים לקרוא באותו הרגע. כך שבעצם קורה שאי אפשר לעבוד גם עם ה-Intellisense וגם לעיין בקוד שמעניין אותנו ונמצא בהמשך השורה, או אולי כמה שורות מתחתינו (תלוי עד כמה גדול חלון ה-Intellisense שלכם, שדרך אגב, אפשר לשנות אותו עם גרירה של העכבר כמו כל חלון רגיל). בדרך כלל אנחנו פשוט נאלצים להקיש...
Headaches with Prefix and Temporary Variables
לפני לא פחות מ-5 שנים, Luca Bolognese כתב פוסט שעסק בשאלה שעלתה ב-C# User Group. השאלה היתה מה יהיה הערך של x, בסוף ביצוע קטע הקוד הבא: int x = 3; x += x++; אם אנחנו זוכרים את ההבדל בין Postfix ו-Prefix, אז לא צריכה להיות יותר מדי בעיה להבין שהתוצאה תהיה בסוף 6 מאחר ואין משמעות ל++ האחרון. כך שלמעשה, ניתן לפשט את הביטוי הזה ל: x = x + x, ועדיין נקבל את אותה ההתנהגות (שימו לב שזה המקרה ב-#C. ב-CPP למשל, אין הגדרה אמיתית לגבי מה הביטוי הזה צריך להחזיר). אם כך, זאת נקודת הפתיחה שלנו. הרשתי לעצמי לקחת...
Forcing JIT Compilation During Runtime
אחד החסרונות/יתרונות של דוט-נט הוא השימוש במנגנון ה-JIT (הלא הוא ה-Just in Time Compilation). למעשה, התהליך שתפקידו להפוך את ה-CIL לשפת מכונה. אפשר להסתכל על המנגנון הזה בתור יתרון מאחר ובצורה הזאת התוכנית מקומפלת על מחשב היעד שבאמת מריץ את התוכנה שלנו. בצורה הזאת, בזמן הקומפילציה ניתן להשתמש בכל היכולות שמעבד היעד תומך בהן. כלומר, בצורה הזאת אנחנו יכולים להגיע לקוד יעיל, ומהיר יותר בהשוואה לאם היינו מקמפלים את התוכנית על מחשב נפרד, בלי לדעת על אילו מחשבים יריצו את התוכנה שלנו (מאחר והיינו צריכים...
Designing Method Overloads
כשזה מגיע לעיצוב API עבור ספריות תוכנה, לא פעם נתקלים במצב בו יש לנו פונקציה שיודעת לקבל מספר לא מבוטל של פרמטרים, אבל בפועל, בד"כ המשתמש לא מעוניין לפרט מה הוא הערך של כל פרמטר ופרמטר, אלא להשתמש בכל מיני "ערכי ברירת מחדל" שהפונקציה יודעת לקבל במקום זאת. התופעה הזאת די נפוצה כשמדובר בפונקציות "פופולריות" שבד"כ נוהגים להשתמש בהם עם סט פרמטרים X, אבל רק לעיתים נדירות רוצים התנהגות מעט שונה, שמצריכה העברה של מספר פרמטרים ספציפים נוספים. הבעיה היא, שברגע שאותה פונקציה מקבלת...
Mysteries with Circular Dependencies
אם יש משהו אחד בסיסי שכולם יודעים על פיתוח תוכנה, הוא שלא ניתן ליצור תלות מעגלית בין פרוייקטים (DLL'ים). אם למשל יש לנו פרוייקט A, שפונה לפרוייקט B, אז לא יהיה ניתן שאותו פרוייקט B יפנה בחזרה לפרוייקט A בתור רפרנס. אם היינו עושים דבר כזה, היתה נוצרת לנו תלות מעגלית בין שני הפרוייקטים. וזה רע, בגלל שכשהקומפיילר ירצה לקמפל את A, הוא יראה שהוא תלוי ב-B, אז הוא יגש ל-B וינסה לקמפל אותו. אבל אז הוא יגלה ש-B למעשה תלוי ב-A, ואז הוא יחזור חלילה עד אין קץ.. האמנם? אם יצא לכם לפשפש מספיק במבנה הספריות...
DateTime.Now Causes Boxing
אולי לא הייתם מיודעים לכך, אבל בכל פעם שאתם פונים ל-DateTime.Now, אתם גורמים בעקיפין הקצאת זכרון דינאמית על ידי Boxing של Int32. הסיבה לכך טמונה עמוק בתוך המימוש של Now, שאם נסתכל מקרוב, נוכל לראות שהוא למעשה עוטף קריאה ל-UtcNow והמרתו לזמן מקומי על ידי קריאה לפונקציה ToLocalTime. עם הקריאה ל-UtcNow אין בעיה, מאחר ובסך הכל נעשית קריאה פנימית ל- GetSystemTimeAsFileTime , שמחזיר את הזמן הנוכחי בפורמט UTC. הבעיה האמיתית טמונה במימוש של ToLocalTime, או למעשה במחלקה CurrentSystemTimeZone בה היא נעזרת...
Macro For Automatically Freezing Threads
לפני 3 חודשים כתבתי פוסט קצר שהציע דרך להקל על עבודת ה-Debug תחת Multithreaded Environments. במקום להתחיל לקבוע Breakpoint'ים עם פילטרים שיגרמו רק לת'רד מסויים להעצר בהם, הצעתי להגדיר Breakpoint רגיל וברגע שנעצרים בו, פשוט לגשת לפאנל ה-Threads ולהקפיא את כל שאר הת'רדים הקיימים (עניין של Select All והקלקה על Freeze). לאחרונה John Robbins פרסם בבלוג שלו פוסט עם דוגמה לפקודת מאקרו שמקפיאה באופן אוטומטי את כל הת'רדים חוץ מהפעיל. למעשה, אוטומטיזציה של הפוסט הקודם שלי. כך שאפשר לחסוך אפילו...
Regions From Hell
אם יש דבר אחד שאני לא אוהב לראות בקוד, זה שימוש מוגזם ב- Region 'ים. הטיעון העיקרי של התומכים בשימוש באותם Region'ים הוא שאפשר להגיע בעזרתם לקוד הרבה יותר "נקי", "מסודר", או חס וחלילה, "קל לתחזוקה". תלוי ביום, ומצב הרוח שלי באותו רגע, אני אוטומטית משיב: הפוך גוטה, הפוך . הדבר היחיד ש-Region'ים יודעים לעשות זה להחביא קוד . מה שלעצמו מרגיש די אבסורדי מאחר ורובנו בדרך כלל נמצאים במירוץ לא נגמר אחרי מסך גדול יותר, רזולוציה מטורפת יותר, פונט קטן יותר - העיקר להכניס...
Code Sample: WorkerResetEvent
כשזה מגיע לתכנות מקבילי, משימה נפוצה למדי היא להשתמש בת'רד נפרד בתוכנית שיטפל בכל מיני קלטים/בקשות שהתוכנית שלנו מקבלת. מה שקורה בדרך כלל הוא שבזמן היצירה של הת'רד, מכניסים אותו לפונקציה עם לולאה אינסופית, ובתוך הלולאה מחכים לקבל Signal על כך ש"יש עבודה" לעשות. בצורה גסה, התבנית הבסיסית נראית כך: AutoResetEvent m_event = new AutoResetEvent ( false ); private void WorkCycles() { while ( true ) { // wait for a signal m_event.WaitOne(); // do work.. } } שימוש ב- AutoResetEvent מאוד...
More Posts Next page »