אופטימיזציה של אפליקציית Go

במדריך הזה נפרס אפליקציית Go לא יעילה בכוונה, שהוגדרה לאסוף נתוני פרופיל. משתמשים בממשק של Profiler כדי לראות את נתוני הפרופיל ולזהות אופטימיזציות פוטנציאליות. לאחר מכן משנים את האפליקציה, פורסים אותה ומעריכים את ההשפעה של השינוי.

לפני שמתחילים

  1. נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  3. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Roles required to select or create a project

    • Select a project: Selecting a project doesn't require a specific IAM role—you can select any project that you've been granted a role on.
    • Create a project: To create a project, you need the Project Creator role (roles/resourcemanager.projectCreator), which contains the resourcemanager.projects.create permission. Learn how to grant roles.

    Go to project selector

  4. מפעילים את Cloud Profiler API.

    תפקידים שנדרשים להפעלת ממשקי API

    כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאה serviceusage.services.enable. איך מקצים תפקידים

    להפעלת ה-API

  5. כדי לפתוח את Cloud Shell, בסרגל הכלים של המסוף Google Cloud , לוחצים על הפעלת Cloud Shell:

    מפעילים את Cloud Shell.

    אחרי כמה דקות ייפתח סשן של Cloud Shell בתוך המסוףGoogle Cloud :

    סשן Cloud Shell.

אפליקציה לדוגמה

המטרה העיקרית היא למקסם את מספר השאילתות לשנייה שהשרת יכול לעבד. מטרה משנית היא לצמצם את השימוש בזיכרון על ידי ביטול הקצאות זיכרון מיותרות.

השרת, באמצעות מסגרת gRPC, מקבל מילה או ביטוי, ואז מחזיר את מספר הפעמים שהמילה או הביטוי מופיעים ביצירות של שייקספיר.

המספר הממוצע של שאילתות לשנייה שהשרת יכול לטפל בהן נקבע על ידי בדיקת העומס על השרת. בכל סבב בדיקות, סימולטור לקוח נקרא ומקבל הוראה להנפיק 20 שאילתות רצופות. בסיום סיבוב, מוצגים מספר השאילתות שנשלחו על ידי סימולטור הלקוח, הזמן שחלף ומספר השאילתות לשנייה (QPS) הממוצע.

קוד השרת לא יעיל בכוונה.

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

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

  1. ב-Cloud Shell, מריצים את הפקודות הבאות:

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git
    cd golang-samples/profiler/shakesapp
    
  2. מריצים את האפליקציה עם הגרסה שמוגדרת ל-1 ומספר הסיבובים שמוגדר ל-15:

    go run . -version 1 -num_rounds 15
    

    אחרי דקה או שתיים, יוצגו נתוני הפרופיל. נתוני הפרופיל נראים כמו בדוגמה הבאה:

    תרשים להבה ראשוני של השימוש בזמן המעבד.

    בצילום המסך, שימו לב שסוג הפרופיל מוגדר ל-CPU time. המשמעות היא שנתוני השימוש במעבד מוצגים בתרשים הלהבות.

    דוגמה לפלט שמוצג ב-Cloud Shell:

    $ go run . -version 1 -num_rounds 15
    2020/08/27 17:27:34 Simulating client requests, round 1
    2020/08/27 17:27:34 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 17:27:34 profiler has started
    2020/08/27 17:27:34 creating a new profile via profiler service
    2020/08/27 17:27:51 Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec
    2020/08/27 17:27:51 Simulating client requests, round 2
    2020/08/27 17:28:10 Simulated 20 requests in 19.02s, rate of 1.051525 reqs / sec
    2020/08/27 17:28:10 Simulating client requests, round 3
    2020/08/27 17:28:29 Simulated 20 requests in 18.71s, rate of 1.068947 reqs / sec
    ...
    2020/08/27 17:44:32 Simulating client requests, round 14
    2020/08/27 17:46:04 Simulated 20 requests in 1m32.23s, rate of 0.216849 reqs / sec
    2020/08/27 17:46:04 Simulating client requests, round 15
    2020/08/27 17:47:52 Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec
    

    בפלט של Cloud Shell מוצג הזמן שחלף בכל איטרציה וקצב הבקשות הממוצע. כשמפעילים את האפליקציה, הרשומה Simulated 20 requests in 17.3s, rate of 1.156069 reqs / sec מציינת שהשרת מבצע בערך בקשה אחת לשנייה. בסיבוב האחרון, השורה 'Simulated 20 requests in 1m48.03s, rate of 0.185134 reqs / sec' (בוצעו 20 בקשות ב-1m48.03s, קצב של 0.185134 בקשות לשנייה) מציינת שהשרת מבצע בערך בקשה אחת כל 5 שניות.

שימוש בפרופילים של זמן מעבד כדי למקסם את מספר השאילתות לשנייה

אחת הדרכים למקסם את מספר השאילתות לשנייה היא לזהות שיטות שדורשות הרבה משאבי CPU ולבצע אופטימיזציה של ההטמעות שלהן. בקטע הזה, תשתמשו בפרופילים של זמן מעבד כדי לזהות שיטה שדורשת הרבה משאבי מעבד בשרת.

זיהוי השימוש בזמן המעבד

המסגרת הראשית של תרשים הלהבות מציגה את סך הזמן של השימוש במעבד על ידי האפליקציה במהלך מרווח האיסוף של 10 שניות:

תצוגה מורחבת של מסגרת השורש בתרשים הלהבות.

בדוגמה הזו, השירות שבו נעשה שימוש הוא 2.37 s. כשהמערכת פועלת על ליבה אחת, שימוש של 2.37 שניות בזמן ה-CPU מתאים לניצול של 23.7% מהליבה הזו. מידע נוסף זמין במאמר בנושא סוגי פרופילים זמינים.

שינוי האפליקציה

הערכת השינוי

כדי להעריך את השינוי:

  1. מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת ל-2:

    go run . -version 2 -num_rounds 40
    

    בקטע מאוחר יותר רואים שבעקבות האופטימיזציה, הזמן שנדרש להרצת סיבוב יחיד קצר בהרבה מהזמן שנדרש להרצת סיבוב יחיד באפליקציה שלא עברה שינוי. כדי לוודא שהאפליקציה פועלת מספיק זמן כדי לאסוף פרופילים ולהעלות אותם, מגדילים את מספר הסיבובים.

  2. מחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:

    • לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר. מידע נוסף זמין במאמר בנושא טווח הזמן.
    • בתפריט גרסה, בוחרים באפשרות 2.

לדוגמה, תרשים הלהבות נראה כך:

תרשים להמחשת השימוש בזמן המעבד בגרסה 2.

בדמות הזו, ערך המסגרת הבסיסית הוא 7.8 s. כתוצאה מהשינוי בפונקציה של התאמת המחרוזת, זמן המעבד שבו נעשה שימוש באפליקציה עלה מ-2.37 שניות ל-7.8 שניות, או שהאפליקציה עברה משימוש ב-23.7% מליבת המעבד לשימוש ב-78% מליבת המעבד.

רוחב הפריים הוא מדד יחסי של השימוש בזמן המעבד. בדוגמה הזו, הרוחב של המסגרת של GetMatchCount מציין שהפונקציה משתמשת בכ-49% מכל הזמן של המעבד שבו נעשה שימוש באפליקציה. בגרף הלהבות המקורי, המסגרת הזו תופסת כ-72% מהרוחב של הגרף. כדי לראות את השימוש המדויק בזמן המעבד, אפשר להשתמש בתיאור הכלים של המסגרת או ברשימת הפונקציות של המיקוד:

רשימת פונקציות של Focus עם נתוני השימוש בזמן המעבד בגרסה 2.

הפלט ב-Cloud Shell מראה שהגרסה ששונתה משלימה כ-5.8 בקשות בשנייה:

$ go run . -version 2 -num_rounds 40
2020/08/27 18:21:40 Simulating client requests, round 1
2020/08/27 18:21:40 Stackdriver Profiler Go Agent version: 20200618
2020/08/27 18:21:40 profiler has started
2020/08/27 18:21:40 creating a new profile via profiler service
2020/08/27 18:21:44 Simulated 20 requests in 3.67s, rate of 5.449591 reqs / sec
2020/08/27 18:21:44 Simulating client requests, round 2
2020/08/27 18:21:47 Simulated 20 requests in 3.72s, rate of 5.376344 reqs / sec
2020/08/27 18:21:47 Simulating client requests, round 3
2020/08/27 18:21:51 Simulated 20 requests in 3.58s, rate of 5.586592 reqs / sec
...
2020/08/27 18:23:51 Simulating client requests, round 39
2020/08/27 18:23:54 Simulated 20 requests in 3.46s, rate of 5.780347 reqs / sec
2020/08/27 18:23:54 Simulating client requests, round 40
2020/08/27 18:23:58 Simulated 20 requests in 3.4s, rate of 5.882353 reqs / sec

לשינוי הקטן באפליקציה היו שתי השפעות שונות:

  • מספר הבקשות לשנייה עלה מפחות מ-1 לשנייה ל-5.8 לשנייה.

  • זמן המעבד לכל בקשה, שמחושב על ידי חלוקת ניצול המעבד במספר הבקשות לשנייה, ירד מ-23.7% ל-13.4%.

    שימו לב שזמן המעבד לכל בקשה ירד, למרות שהשימוש בזמן המעבד עלה מ-2.37 שניות, שמתאים לניצול של 23.7% מליבת מעבד אחת, ל-7.8 שניות, או ל-78% מליבת מעבד.

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

בקטע הזה נסביר איך אפשר להשתמש בפרופילים של הערימה והערימה שהוקצתה כדי לזהות שיטה שדורשת הקצאה רבה באפליקציה:

  • פרופילים של ערימות מציגים את כמות הזיכרון שהוקצתה בערימה של התוכנית ברגע שבו הפרופיל נאסף.

  • פרופילים של ערימות שהוקצו מציגים את כמות הזיכרון הכוללת שהוקצתה בערימה של התוכנית במהלך המרווח שבו הפרופיל נאסף. אם מחלקים את הערכים האלה ב-10 שניות, שהוא מרווח האיסוף של הפרופיל, אפשר לפרש אותם כשיעורי הקצאה.

הפעלה של איסוף נתונים של פרופיל הערימה

  1. מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת ל-3 ומפעילים את איסוף הפרופילים של הערימה והערימה שהוקצתה.

    go run . -version 3 -num_rounds 40 -heap -heap_alloc
    
  2. מחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:

    • לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר.
    • בתפריט גרסה, בוחרים באפשרות 3.
    • בתפריט Profiler type, בוחרים באפשרות Allocated heap.

    לדוגמה, תרשים הלהבות נראה כך:

    תרשים להבות של פרופילים של ערימה שהוקצו לגרסה 3.

זיהוי קצב הקצאת זיכרון בערימה (heap allocation)

במסגרת הבסיס מוצג סכום הערימה שהוקצה במהלך 10 השניות שבהן נאסף פרופיל, בממוצע בכל הפרופילים. בדוגמה הזו, במסגרת הבסיס מוצג שבאופן ממוצע הוקצו 1.535‎ GiB של זיכרון.

שינוי האפליקציה

הערכת השינוי

כדי להעריך את השינוי:

  1. מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת כ-4:

    go run . -version 4 -num_rounds 60 -heap -heap_alloc
    
  2. מחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:

    • לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר.
    • בתפריט גרסה, בוחרים באפשרות 4.
    • בתפריט Profiler type, בוחרים באפשרות Allocated heap.
  3. כדי לכמת את ההשפעה של שינוי readFiles על קצב הקצאת זיכרון בערימה (heap allocation), משווים את תמונות המצב של הערימה שהוקצו לגרסה 4 לאלה שנאספו לגרסה 3:

    השוואה בין פרופילי ה-heap שהוקצו בגרסאות 4 ו-3.

    בתיאור כלי העזרה של מסגרת הבסיס מוצג שבגרסה 4, כמות הזיכרון הממוצעת שהוקצתה במהלך איסוף הפרופיל ירדה ב-1.301GiB, בהשוואה לגרסה 3. בהסבר הקצר על readFiles.func1 מוצגת ירידה של 1.045‎ GiB:

    השוואה של תיאור הכלי readfiles עבור סוג הפרופיל של ה-heap שהוקצה.

  4. כדי לכמת את ההשפעה על איסוף האשפה, מגדירים השוואה של פרופילים של זמן המעבד (CPU). בצילום המסך הבא, מסנן מוחל כדי להציג את המחסניות של איסוף האשפה של Go‏ runtime.gcBgMarkWorker.*. בצילום המסך אפשר לראות ששימוש המעבד (CPU) למנגנון איסוף ירד מ-16.8% ל-4.97%.

    השוואה בין השימוש בזמן המעבד של תהליך איסוף האשפה ברקע בגרסה 4 לבין גרסה 3.

  5. כדי לבדוק אם השינוי משפיע על מספר הבקשות לשנייה שהאפליקציה מטפלת בהן, צריך להציג את הפלט ב-Cloud Shell. בדוגמה הזו, גרסה 4 משלימה עד 15 בקשות בשנייה, שזה הרבה יותר מ-5.8 הבקשות בשנייה של גרסה 3:

    $ go run . -version 4 -num_rounds 60 -heap -heap_alloc
    2020/08/27 21:51:42 Simulating client requests, round 1
    2020/08/27 21:51:42 Stackdriver Profiler Go Agent version: 20200618
    2020/08/27 21:51:42 profiler has started
    2020/08/27 21:51:42 creating a new profile via profiler service
    2020/08/27 21:51:44 Simulated 20 requests in 1.47s, rate of 13.605442 reqs / sec
    2020/08/27 21:51:44 Simulating client requests, round 2
    2020/08/27 21:51:45 Simulated 20 requests in 1.3s, rate of 15.384615 reqs / sec
    2020/08/27 21:51:45 Simulating client requests, round 3
    2020/08/27 21:51:46 Simulated 20 requests in 1.31s, rate of 15.267176 reqs / sec
    ...
    

    יכול להיות שהעלייה במספר השאילתות לשנייה שהאפליקציה משרתת נובעת מכך שפחות זמן מושק במנגנון איסוף.

  • כדי להבין טוב יותר את ההשפעה של השינוי על readFiles, אפשר לעיין בפרופילים של ה-heap. השוואה בין פרופילי הערימה של גרסה 4 לבין פרופילי הערימה של גרסה 3 מראה שהשימוש בערימה ירד מ-70.95MiB ל-18.47MiB:

    השוואה בין השימוש ב-heap בגרסה 4 לבין השימוש ב-heap בגרסה 3.

סיכום

במדריך הזה השתמשנו בפרופילים של זמן מעבד ושל הקצאת זיכרון בערימה כדי לזהות אופטימיזציות פוטנציאליות באפליקציה. המטרה הייתה למקסם את מספר הבקשות לשנייה ולבטל הקצאות מיותרות.

  • באמצעות פרופילים של זמן מעבד, זוהתה פונקציה שצורכת הרבה משאבים מהמעבד. אחרי יישום שינוי פשוט, קצב הבקשות של השרת עלה ל-5.8 לשנייה, לעומת קצב של בקשה אחת לשנייה לפני השינוי.

  • באמצעות פרופילים של הקצאת זיכרון בערימה, זוהתה הפונקציה readFilesshakesapp/server.go כבעלת קצב הקצאה גבוה. אחרי האופטימיזציה של readFiles, קצב הבקשות של השרת עלה ל-15 בקשות לשנייה, וכמות הזיכרון הממוצעת שהוקצתה במהלך איסוף הפרופיל למשך 10 שניות ירדה ב-1.301‎ GiB.

המאמרים הבאים

  • מידע על האופן שבו הפרופילים נאספים ונשלחים לGoogle Cloud פרויקט זמין במאמר בנושא איסוף פרופילים.

  • אפשר גם לקרוא את המשאבים שלנו בנושא DevOps ולעיין בתוכנית המחקר שלנו.

מידע על הפעלת סוכן Cloud Profiler זמין במאמרים הבאים: