יום שישי, 27 בספטמבר 2013

Memory Allocation in the CLR

אז מה קורה בזכרון כאשר אנחנו יוצרים אובייקט מסוג ReferenceType בזיכרון?

נניח ויש לנו אובייקט מסוג Student שמכיל מספר פונקציות ומספר משתנים.

לאחר שנבצע ()new Student , הייצוג של האובייקט בזיכרון יראה כך:


ועכשיו להסבר:

כשנאתחל את האובייקט - מכיוון שהוא מסוג ReferenceType ,יוקצה לו מקום על גבי ה GC Heap ויהיה לעברו מצביע (בציור הנ"ל המצביע הוא על גבי הStack, אך לא מן הנמנע שהמצביע יהיה על גבי ה GC Heap)

לכל אובייקט שנמצא על גבי ה GC Heap יש שני שדות שמתווספים אוטומטית לאובייקט (ושגודל של כל אחד מהם 4bytes) השדות הם:

  1.   Sync Block Index -SBI - מצביע לטבלת synchronisation הבנויה מ Sync Blocks. הטבלה מכילה את המידע האם בוצע על האובייקט lock או לא. כל פעם שמבוצע lock על האובייקט, נוצר Sync Block חדש שמכיל מידע אודות ה lock.
  2.  Type Object Pointer - TOP- מצביע למקום שנקרא Method Table הנמצא על ה High Frequency Heap, המתאר את האובייקט. (ארחיב בהמשך)
High Frequency Heap- מקום על הHeap המכיל נתונים שניגשים אליהם בתדירות גבוהה. הוא לא מנוהל ע"י ה GC, הוא נוצר עם יצירת ה AppDomain, לכל AppDomain יש אחד כזה, ומטבע הדברים הוא מתנקה כאשר ה AppDomain "מת". דבר חשוב מאוד לדעת - כל המשתנים הסטטיים נמצאים על גבי ה High Frequency Heap, ומכאן שהם לא מתנקים ע"י ה GC..

Method Table - לפני שאנחנו יוצרים את ה Instance הראשון של האובייקט, כאשר כל ה Class וה Interface נטענים לראשונה ל AppDomain, נוצר על גבי ה High Frequency Heap ה Method Table, שמכיל את כל הנתונים על האובייקט, כמו: מאיזה type הוא, איזה פונקציות הוא מכיל (כולל כל ה ctor) ואת המשתנים הסטטיים.
מכאן הקומפיילר יודע איזה פונקציה האובייקט יפעיל (במקרים של הורשה למשל).
 לדוגמא: אם ניצור אובייקט בלי שנדרוס את ToString, בMethod Table תופיע הפונקציה ToString שה MethodDescription שלו יהיה שייך למחלקה System.Object, ומכאן - כאשר האובייקט יפעיל את ToString, אז יופעל ה ToString הדיפולטי שהמחלקה שלנו יורשת ממנו (במקרה הנ"ל זה System.Object). אך אם ניצור אובייקט שכתבנו לו פונקציה שדורסת את ToString, אז ה MethodDescription שלנו יהיה שייך למחלקה שלנו (Student), וכאשר האובייקט יפעיל את ToString, אז יופעל ה ToString שאנחנו כתבנו..


מי שרוצה לרדת עוד יותר לעומק הדברים, מאמר מצוין נמצא כאן

תהנו!

יום שישי, 20 בספטמבר 2013

יצירת Performance Counter

בהמשך לפוסטים האחרונים שלי על Tracing, הנה דרך מצוינת לאסוף סטטיסטיקות על האפליקציה שלנו:

בכל זמן נתון מערכת ההפעלה של Windows אוספת סטטיסטיקות על המערכת (כגון CPU, IO, סטטיסטיקות על IIS ועוד המון אחרים..). את הסטטיסטיקות אפשר לראות דרך ה Perfmon.exe.
בדרך זו אפשר ללמוד המון על המערכת שלנו (למשל כאשר היא רצה בסביבת Production).
יתרון גדול שמערכת ההפעלה מאפשרת לנו הוא שאנחנו יכולים ליצור Counter's משלנו, וכך יכולים לעקוב אחר ביצועי האפליקציה שלנו (למשל נוכל לראות כמה זמן בממוצע לוקח לשרות שלנו לעבד בקשה, כמו login בוצעו לאפליקציה, וכל סטטיסטיקה אחרת שאפשר לדמיין).

באמצעות ה PerformanceCounter class ש .Net מספק לנו, אנחנו יכולים לגשת לנתוני Counter שונים, ואף ליצור Counter חדשים לאפליקציה שלנו.

יש כל מיני Counter Types, אך השכיחים ביותר:
  1. NumberOfItems32- סופר מספר פריטים (למשל אפשר לספור כמה בקשות בוצעו)
  2. AverageTimer32- סופר את הזמן הממוצע שנדרש לפעולה מסוימת להיות מבוצעת
  3. RateOfCountsPerSecond32- סופר את מספר הפעולות שבוצעו בכל שנייה
יצירת Performance Counter חדש:

הדרך הראשונה (והפחות עדיפה) היא דרך ה Server Explorer  ב VS:



הדרך השניה היא ליצור באמצעות קוד שנכתוב:

ניצור את ה Counter באמצעות הקוד (ככה שנראה אותו ב Perfmon..):

 
        if (!PerformanceCounterCategory.Exists("MyNewCategory"))
            {
                CounterCreationDataCollection counters = new CounterCreationDataCollection();

                //counting totals: PerformanceCounterType.NumberOfItems32
                CounterCreationData totalOperations = new CounterCreationData();
                totalOperations.CounterName = "# operations executed";
                totalOperations.CounterType = PerformanceCounterType.NumberOfItems32;
                counters.Add(totalOperations);

                //counting operations per second: PerformanceCounterType.RateOfCountsPerSecond32
                CounterCreationData opsPerSecond = new CounterCreationData();
                opsPerSecond.CounterName = "# operations / sec";
                opsPerSecond.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
                counters.Add(opsPerSecond);

                //counting average time per operation: PerformanceCounterType.AverageTimer32
                CounterCreationData avgDuration = new CounterCreationData();
                avgDuration.CounterName = "average time per operation";
                avgDuration.CounterType = PerformanceCounterType.AverageTimer32;
                counters.Add(avgDuration);

                //base counter for counting average time per operation: PerformanceCounterType.AverageBase
                CounterCreationData avgDurationBase = new CounterCreationData();
                avgDurationBase.CounterName = "average time per operation base";
                avgDurationBase.CounterType = PerformanceCounterType.AverageBase;
                counters.Add(avgDurationBase);

                // create new category with the counters above
                PerformanceCounterCategory.Create("MyNewCategory", 
                        "Sample category", counters);
            }

שימו לב!! אם יצרתם  AverageTimer אז חייבים מיד אחריו ליצור AverageBase.

לאחר שנריץ את הקוד הנ"ל יהיה אפשר להכנס ל Perfmon ולקבל:


עכשיו כל אפליקציה תוכל לכתוב/לקרוא נתונים על ה Counter שיצרנו (MyNewCategory)

יצירת האובייקט של ה Performance Counter באפליקציה שלנו:

כעת באפליקציה שלנו נרצה לאתחל את ה Counter's כדי שנוכל להתממשק לקטגוריה שיצרנו

     _TotalOps = new PerformanceCounter();
            _TotalOps.CategoryName = "MyNewCategory";
            _TotalOps.CounterName = "# operations executed";
            _TotalOps.MachineName = ".";
            _TotalOps.ReadOnly = false;

            _OpsPerSecond = new PerformanceCounter();
            _OpsPerSecond.CategoryName = "MyNewCategory";
            _OpsPerSecond.CounterName = "# operations / sec";
            _OpsPerSecond.MachineName = ".";
            _OpsPerSecond.ReadOnly = false;

            //another way to create...
            _AvgDuration = new PerformanceCounter("MyNewCategory",
            "average time per operation",
            false);

            _AverageDurationBase = new PerformanceCounter("MyNewCategory",
            "average time per operation base",
            false);

שימו לב!! כשאתחלנו את המשתנים באפליקציה שלנו- אם אחד מהפרמטרים לא תואם את מה שקיים על השרת (על פי מה שיצרנו בשלב הראשון- למשל ה Category שונה או ה CounterName שונה אז נקבל InvalidOperationException

הכנסת הנתונים ל Counter:

מה שנותר לנו כעת זה להכניס נתונים ל Counter..

לדוגמא בעת ריצת האפליקציה הבאה:

      Stopwatch s = new Stopwatch();
    
            for (int i = 0; i < 1000; i++)
            {
                s.Start();
                
                // simply increment the counters
                _TotalOps.Increment();

                _OpsPerSecond.Increment();

                Thread.Sleep(300+10*i);

                // increment the timer 
                _AvgDuration.IncrementBy(s.ElapsedTicks);
                _AverageDurationBase.IncrementBy(1);
            
                s.Stop();
                s.Reset();
            }      
יהיה אפשר לראות ב Perfmon את הסטטיסטיקה הבאה (למשל):


תהנו!

יום חמישי, 19 בספטמבר 2013

Best practices for Instrumenting Biztalk solution

בהמשך לפוסט האחרון שלי על Tracing high performance applications , היכן אנחנו צריכים לכתוב Tracing?

Pipeline Components
  • בעת כניסה ויציאה מהקומפוננטה.
  • פירוט של ה runtime exception שנזרק.
  • מדידת הזמן שלקח לפונקצייה מסוימת לרוץ/ במקרים מסוימים נרצה לדעת כמה לקח לכל הקומפוננטה לרוץ
  • ביצועTrace למצבים מסוימים בתוך הקומפוננטה (בתוך if) או למשתנה מסוים (שיעזור לנו בהמשך במקרה של בחינת תקלות)
Biztalk Maps
  • נרצה לדעת ערכים מסוימים שאנחנו רוצים למפות. נבצע באמצעות Scripting functoid. 

Biztalk Orchestrations

ביצוע Trace לאורכסטרציות זהו המפתח למעקב, דיאגנוסטיקה ולפתרון תקלות. זאת במיוחד כי חלק מהאורכסטרציות שלנו הופכות להיות למאוד מסובכות. (גם בעת פיתוח יותר קל לדאבג אורכסטרציות שיש עליהם Tracing)
נבצע:
  • מיד כאשר נכנסים לאורכסטרציה (לאחר receive shape)
  • ביצועTrace למצבים מסוימים בתוך האורכסטרציה (בתוך if) או למשתנה מסוים (שיעזור לנו בהמשך במקרה של בחינת תקלות)
  • פירוט של ה runtime exception שנזרק.
  • מדידת הזמן שלקח ל scope מסויים לרוץ/ במקרים מסוימים נרצה לדעת כמה לקח לכל האורכסטרציה לרוץ
  • לפני שיוצאים מהאורכסטרציה (לפני ה exit point)
מעקב מהנה!