אופטימיזציה של אפליקציית Go
במדריך הזה נפרס אפליקציית Go לא יעילה בכוונה, שהוגדרה לאסוף נתוני פרופיל. משתמשים בממשק של Profiler כדי לראות את נתוני הפרופיל ולזהות אופטימיזציות פוטנציאליות. לאחר מכן משנים את האפליקציה, פורסים אותה ומעריכים את ההשפעה של השינוי.
לפני שמתחילים
- נכנסים לחשבון Google Cloud . אם אתם משתמשים חדשים ב- Google Cloud, צרו חשבון כדי שתוכלו להעריך את הביצועים של המוצרים שלנו בתרחישים מהעולם האמיתי. לקוחות חדשים מקבלים בחינם גם קרדיט בשווי 300$ להרצה, לבדיקה ולפריסה של עומסי העבודה.
-
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 theresourcemanager.projects.createpermission. Learn how to grant roles.
-
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 theresourcemanager.projects.createpermission. Learn how to grant roles.
-
מפעילים את Cloud Profiler API.
תפקידים שנדרשים להפעלת ממשקי API
כדי להפעיל ממשקי API, צריך את תפקיד ה-IAM 'אדמין של Service Usage' (
roles/serviceusage.serviceUsageAdmin), שכולל את ההרשאהserviceusage.services.enable. איך מקצים תפקידים - כדי לפתוח את Cloud Shell, בסרגל הכלים של המסוף Google Cloud , לוחצים על הפעלת Cloud Shell:

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

אפליקציה לדוגמה
המטרה העיקרית היא למקסם את מספר השאילתות לשנייה שהשרת יכול לעבד. מטרה משנית היא לצמצם את השימוש בזיכרון על ידי ביטול הקצאות זיכרון מיותרות.
השרת, באמצעות מסגרת gRPC, מקבל מילה או ביטוי, ואז מחזיר את מספר הפעמים שהמילה או הביטוי מופיעים ביצירות של שייקספיר.
המספר הממוצע של שאילתות לשנייה שהשרת יכול לטפל בהן נקבע על ידי בדיקת העומס על השרת. בכל סבב בדיקות, סימולטור לקוח נקרא ומקבל הוראה להנפיק 20 שאילתות רצופות. בסיום סיבוב, מוצגים מספר השאילתות שנשלחו על ידי סימולטור הלקוח, הזמן שחלף ומספר השאילתות לשנייה (QPS) הממוצע.
קוד השרת לא יעיל בכוונה.
הפעלת האפליקציה לדוגמה
מורידים ומריצים את האפליקציה לדוגמה:
ב-Cloud Shell, מריצים את הפקודות הבאות:
git clone https://github.com/GoogleCloudPlatform/golang-samples.git cd golang-samples/profiler/shakesappמריצים את האפליקציה עם הגרסה שמוגדרת ל-
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% מהליבה הזו. מידע נוסף זמין במאמר בנושא סוגי פרופילים זמינים.
שינוי האפליקציה
הערכת השינוי
כדי להעריך את השינוי:
מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת ל-
2:go run . -version 2 -num_rounds 40בקטע מאוחר יותר רואים שבעקבות האופטימיזציה, הזמן שנדרש להרצת סיבוב יחיד קצר בהרבה מהזמן שנדרש להרצת סיבוב יחיד באפליקציה שלא עברה שינוי. כדי לוודא שהאפליקציה פועלת מספיק זמן כדי לאסוף פרופילים ולהעלות אותם, מגדילים את מספר הסיבובים.
מחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:
- לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר. מידע נוסף זמין במאמר בנושא טווח הזמן.
- בתפריט גרסה, בוחרים באפשרות 2.
לדוגמה, תרשים הלהבות נראה כך:
בדמות הזו, ערך המסגרת הבסיסית הוא 7.8 s. כתוצאה מהשינוי בפונקציה של התאמת המחרוזת, זמן המעבד שבו נעשה שימוש באפליקציה עלה מ-2.37 שניות ל-7.8 שניות, או שהאפליקציה עברה משימוש ב-23.7% מליבת המעבד לשימוש ב-78% מליבת המעבד.
רוחב הפריים הוא מדד יחסי של השימוש בזמן המעבד. בדוגמה הזו, הרוחב של המסגרת של GetMatchCount מציין שהפונקציה משתמשת בכ-49% מכל הזמן של המעבד שבו נעשה שימוש באפליקציה.
בגרף הלהבות המקורי,
המסגרת הזו תופסת כ-72% מהרוחב של הגרף.
כדי לראות את השימוש המדויק בזמן המעבד, אפשר להשתמש בתיאור הכלים של המסגרת או ברשימת הפונקציות של המיקוד:
הפלט ב-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 שניות, שהוא מרווח האיסוף של הפרופיל, אפשר לפרש אותם כשיעורי הקצאה.
הפעלה של איסוף נתונים של פרופיל הערימה
מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת ל-
3ומפעילים את איסוף הפרופילים של הערימה והערימה שהוקצתה.go run . -version 3 -num_rounds 40 -heap -heap_allocמחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:
- לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר.
- בתפריט גרסה, בוחרים באפשרות 3.
- בתפריט Profiler type, בוחרים באפשרות Allocated heap.
לדוגמה, תרשים הלהבות נראה כך:
זיהוי קצב הקצאת זיכרון בערימה (heap allocation)
במסגרת הבסיס מוצג סכום הערימה שהוקצה במהלך 10 השניות שבהן נאסף פרופיל, בממוצע בכל הפרופילים. בדוגמה הזו, במסגרת הבסיס מוצג שבאופן ממוצע הוקצו 1.535 GiB של זיכרון.
שינוי האפליקציה
הערכת השינוי
כדי להעריך את השינוי:
מריצים את האפליקציה עם גרסת האפליקציה שמוגדרת כ-
4:go run . -version 4 -num_rounds 60 -heap -heap_allocמחכים לסיום הבקשה ואז צופים בנתוני הפרופיל של הגרסה הזו של האפליקציה:
- לוחצים על עכשיו כדי לטעון את נתוני הפרופיל העדכניים ביותר.
- בתפריט גרסה, בוחרים באפשרות 4.
- בתפריט Profiler type, בוחרים באפשרות Allocated heap.
כדי לכמת את ההשפעה של שינוי
readFilesעל קצב הקצאת זיכרון בערימה (heap allocation), משווים את תמונות המצב של הערימה שהוקצו לגרסה 4 לאלה שנאספו לגרסה 3:
בתיאור כלי העזרה של מסגרת הבסיס מוצג שבגרסה 4, כמות הזיכרון הממוצעת שהוקצתה במהלך איסוף הפרופיל ירדה ב-1.301GiB, בהשוואה לגרסה 3. בהסבר הקצר על
readFiles.func1מוצגת ירידה של 1.045 GiB:
כדי לכמת את ההשפעה על איסוף האשפה, מגדירים השוואה של פרופילים של זמן המעבד (CPU). בצילום המסך הבא, מסנן מוחל כדי להציג את המחסניות של איסוף האשפה של Go
runtime.gcBgMarkWorker.*. בצילום המסך אפשר לראות ששימוש המעבד (CPU) למנגנון איסוף ירד מ-16.8% ל-4.97%.
כדי לבדוק אם השינוי משפיע על מספר הבקשות לשנייה שהאפליקציה מטפלת בהן, צריך להציג את הפלט ב-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:
סיכום
במדריך הזה השתמשנו בפרופילים של זמן מעבד ושל הקצאת זיכרון בערימה כדי לזהות אופטימיזציות פוטנציאליות באפליקציה. המטרה הייתה למקסם את מספר הבקשות לשנייה ולבטל הקצאות מיותרות.
באמצעות פרופילים של זמן מעבד, זוהתה פונקציה שצורכת הרבה משאבים מהמעבד. אחרי יישום שינוי פשוט, קצב הבקשות של השרת עלה ל-5.8 לשנייה, לעומת קצב של בקשה אחת לשנייה לפני השינוי.
באמצעות פרופילים של הקצאת זיכרון בערימה, זוהתה הפונקציה
readFilesshakesapp/server.goכבעלת קצב הקצאה גבוה. אחרי האופטימיזציה שלreadFiles, קצב הבקשות של השרת עלה ל-15 בקשות לשנייה, וכמות הזיכרון הממוצעת שהוקצתה במהלך איסוף הפרופיל למשך 10 שניות ירדה ב-1.301 GiB.
המאמרים הבאים
מידע על האופן שבו הפרופילים נאספים ונשלחים לGoogle Cloud פרויקט זמין במאמר בנושא איסוף פרופילים.
אפשר גם לקרוא את המשאבים שלנו בנושא DevOps ולעיין בתוכנית המחקר שלנו.
- יצירת פרופילים של אפליקציות Go
- יצירת פרופילים של אפליקציות Java
- יצירת פרופיל של אפליקציות Node.js
- יצירת פרופילים של אפליקציות Python
- יצירת פרופילים של אפליקציות שפועלות מחוץ ל- Google Cloud