پروفایلینگ برای شناسایی بخشهای گرانقیمت یا پرتکرار کد بسیار مفید است. رانتایم Go دادههای پروفایلینگ را در قالبی که ابزار مصورسازی pprof انتظار دارد، فراهم میکند. این دادهها را میتوان در طول تست از طریق go test یا از طریق اندپوینتهایی که توسط پکیج net/http/pprof ارائه میشوند، جمعآوری کرد. کاربران باید دادههای پروفایلینگ را جمعآوری کرده و با استفاده از ابزارهای pprof مسیرهای اصلی و پرهزینه کد را فیلتر و مصورسازی کنند.
پروفایلهای پیشفرض ارائه شده توسط پکیج runtime/pprof
-
cpu: پروفایل CPU مشخص میکند که برنامه زمان خود را کجا و در حین مصرف فعال سیکلهای CPU میگذراند (برخلاف زمانهایی که در حالت خواب یا در انتظار I/O است).
-
heap: پروفایل Heap نمونههای تخصیص حافظه (Memory Allocations) را گزارش میکند؛ این پروفایل برای نظارت بر مصرف فعلی و تاریخی حافظه و بررسی نشتی حافظه (Memory Leak) استفاده میشود.
-
threadcreate: پروفایل ایجاد ترد، بخشهایی از برنامه را گزارش میکند که منجر به ایجاد تردهای جدید در سطح سیستمعامل (OS Threads) میشوند.
-
goroutine: پروفایل گوروتین، استک تریس (Stack Traces) تمام گوروتینهای فعال را گزارش میکند.
-
block: پروفایل بلوک نشان میدهد که گوروتینها کجا منتظر پریمیتیوهای همگامسازی (از جمله کانالهای تایمر) مسدود میشوند. این پروفایل بهصورت پیشفرض فعال نیست؛ برای فعالسازی آن از
runtime.SetBlockProfileRateاستفاده کنید. -
mutex: پروفایل Mutex رقابتها و قفلشدگیهای متقابل (Lock Contentions) را گزارش میکند. زمانی که فکر میکنید CPU شما به دلیل تنازع روی Mutex بهطور کامل استفاده نمیشود، از این پروفایل استفاده کنید. این پروفایل بهصورت پیشفرض فعال نیست؛ برای فعالسازی آن به
runtime.SetMutexProfileFractionمراجعه کنید.
سوالات متداول
چه پروفایرهای دیگری میتوانم برای برنامههای Go استفاده کنم؟
روی سیستمعامل لینوکس، ابزارهای perf میتوانند برای پروفایل کردن برنامههای گو استفاده شوند. perf میتواند کدهای cgo/SWIG و کرنل را پروفایل و آنوایند (Unwind) کند، بنابراین برای به دست آوردن بینش نسبت به گلوگاههای عملکردی بومی/کرنل مفید است. روی macOS نیز میتوان از مجموعه ابزارهای Instruments استفاده کرد.
آیا میتوانم سرویسهای پروداکشن (Production) خود را پروفایل کنم؟
بله. پروفایل کردن برنامهها در محیط پروداکشن امن است، اما فعال کردن برخی پروفایلها (مانند پروفایل CPU) هزینه دارد و باید انتظار افت عملکرد را داشته باشید. جریمه عملکردی را میتوان با اندازهگیری سربار (Overhead) پروفایر قبل از روشن کردن آن در پروداکشن تخمین زد.
توصیه میشود سرویسهای پروداکشن خود را بهصورت دورهای پروفایل کنید. بهویژه در سیستمی با چندین کپی (Replica) از یک پروسه، انتخاب تصادفی یک کپی بهصورت دورهای گزینه امنی است. یک پروسه پروداکشن را انتخاب کنید، آن را به مدت X ثانیه به ازای هر Y ثانیه پروفایل کرده و نتایج را برای مصورسازی و تحلیل ذخیره کنید؛ سپس این کار را به طور دورهای تکرار کنید. نتایج را میتوان به صورت دستی و/یا خودکار برای یافتن مشکلات بررسی کرد. جمعآوری پروفایلها ممکن است با یکدیگر تداخل داشته باشد، بنابراین توصیه میشود در هر زمان تنها یک پروفایل را جمعآوری کنید.
بهترین راهها برای مصورسازی دادههای پروفایلینگ چیست؟
ابزارهای Go مصورسازی متنی، گراف و callgrind از دادههای پروفایل را با استفاده از go tool pprof فراهم میکنند. برای دیدن آنها در عمل، مقاله Profiling Go programs را مطالعه کنید.
-
نمایش متنی (Text): فهرست کردن پرهزینهترین فراخوانیها به صورت متن.
-
نمایش گراف (Graph): مصورسازی پرهزینهترین فراخوانیها به صورت گراف.
-
نمایش وبلیست (Weblist): نمای Weblist بخشهای گرانقیمت کد را خط به خط در یک صفحه HTML نمایش میدهد. در این حالت هزینه هر خط مشخص میشود.
-
نمایش گراف شعلهای (Flame Graphs): روش دیگر، نمودارهای شعلهای هستند. این نمودارها به شما اجازه میدهند در یک مسیر نیاکانی خاص حرکت کنید تا بتوانید روی بخشهای خاصی از کد زوم کنید/خارج شوید.
آیا به پروفایرهای توکار (Built-in) محدود هستم؟
علاوه بر مواردی که توسط رانتایم ارائه شده است، توسعهدهندگان میتوانند پروفایلهای سفارشی خود را از طریق pprof.Profile ایجاد کرده و با ابزارهای موجود آنها را بررسی کنند.
آیا میتوانم هندلرهای پروفایر (/debug/pprof/...) را روی مسیر و پورت متفاوتی سرویس دهم؟
بله. پکیج net/http/pprof هندلرهای خود را بهصورت پیشفرض روی دیفالت mux ثبت میکند، اما شما میتوانید با استفاده از هندلرهای صادرشده (Exported) از پکیج، آنها را به دلخواه خود ثبت کنید.
برای مثال، کد زیر هندلر pprof.Profile را روی پورت 7777 و مسیر /custom_debug_path/profile سرویس میدهد:
package main
import (
"log"
"net/http"
"net/http/pprof"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/custom_debug_path/profile", pprof.Profile)
log.Fatal(http.ListenAndServe(":7777", mux))
}
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: منبع مستندات