أداء Go: كيفية تحليل الأداء وتسريع الكود

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

كود Go الخاص بك يعمل ببطء؟ حان الوقت للنظر تحت الغطاء

\n\n

لقد كتبت خدمة بلغة Go. إنها تعمل. ولكن عندما يصل الحمل إلى 10,000 طلب في الثانية، يبدأ الخادم في "الاختناق". موقف مألوف؟ تشتهر Go بأدائها "خارج الصندوق"، ولكن حتى في هذه اللغة الأنيقة هناك بعض المزالق. تسربات الغوروتين، إدارة غير فعالة للذاكرة، تخصيصات زائدة - كل هذا يقتل السرعة.

\n\n

في هذه المقالة، سنشرح كيف يساعد تحليل أداء Go في العثور على نقاط الضعف، وسنعرض تقنيات محددة لتحسين تطبيقات Go. ستتعلم كيفية استخدام الأدوات المدمجة pprof و go test -bench لتحويل تطبيقك إلى صاروخ. هيا بنا!

\n\n

أدوات المحلل: pprof و trace

\n\n

توفر Go أداتين قويتين لتحليل الأداء: pprof (تحليل وحدة المعالجة المركزية، الذاكرة، الأقفال) و go tool trace (تحليل التزامن وأحداث جامع القمامة).

\n\n

دمج pprof في خادم HTTP

\n\n

أسهل طريقة لبدء التحليل هي إضافة net/http/pprof إلى خادمك. هذا يتطلب سطر استيراد واحد فقط:

\n\n
import (\n    _ "net/http/pprof"\n    "net/http"\n)\n\nfunc main() {\n    http.ListenAndServe(":8080", nil)\n}
\n\n

الآن، الملفات الشخصية متاحة على العنوان /debug/pprof/. لأخذ ملف تعريف لوحدة المعالجة المركزية لمدة 30 ثانية، قم بتنفيذ:

\n\n
go tool pprof http://localhost:8080/debug/pprof/profile?seconds=30
\n\n

بعد جمع البيانات، سيتم تشغيل وحدة تحكم تفاعلية. الأمر top15 سيظهر الوظائف الأكثر استهلاكًا:

\n\n
(pprof) top15\nShowing nodes accounting for 3.50s, 87.50% of 4.00s total\n      flat  flat%   sum%        cum   cum%\n     1.20s 30.00% 30.00%      1.50s 37.50%  main.heavyComputation
\n\n

هل ترى main.heavyComputation؟ هذا هو مرشحنا للتحسين. للتصور، استخدم web (إذا كان Graphviz مثبتًا) - ستحصل على رسم بياني للاستدعاءات.

\n\n

تحليل الذاكرة: البحث عن التسريبات

\n\n

تسريبات الذاكرة في Go غالبًا ما تكون تسريبات غوروتين أو مراجع منسية لكائنات كبيرة. لنأخذ ملف تعريف الكومة:

\n\n
go tool pprof http://localhost:8080/debug/pprof/heap
\n\n

الأمر alloc_objects سيظهر أي الوظائف تنشئ أكبر عدد من الكائنات:

\n\n
(pprof) alloc_objects\n    2.50GB  100%  100%      2.50GB  100%  main.createLargeSlice
\n\n

هكذا وجدنا وظيفة تخصص 2.5 جيجابايت من الذاكرة! ربما يتم إنشاء شريحة ضخمة يمكن إعادة استخدامها باستخدام sync.Pool.

\n\n

كتابة المعايير: خطوتك الأولى نحو التحسين

\n\n

قبل التحسين، يجب القياس. معايير Go هي الأداة القياسية لقياس أداء الوظائف الفردية.

\n\n

مثال: مقارنة دمج السلاسل النصية

\n\n

لنفترض أننا ندمج سلاسل نصية في حلقة. الخيار السيئ:

\n\n
func ConcatSlow(parts []string) string {\n    result := ""\n    for _, p := range parts {\n        result += p\n    }\n    return result\n}
\n\n

الخيار الجيد هو استخدام strings.Builder:

\n\n
func ConcatFast(parts []string) string {\n    var b strings.Builder\n    for _, p := range parts {\n        b.WriteString(p)\n    }\n    return b.String()\n}
\n\n

لنكتب معيارًا (ملف concat_test.go):

\n\n
func BenchmarkConcatSlow(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatSlow(parts)\n    }\n}\n\nfunc BenchmarkConcatFast(b *testing.B) {\n    parts := []string{"Hello", " ", "World", "!", " ", "From", " ", "Go"}\n    for i := 0; i < b.N; i++ {\n        ConcatFast(parts)\n    }\n}
\n\n

نقوم بتشغيل:

\n\n
go test -bench=. -benchmem
\n\n

النتيجة:

\n\n
BenchmarkConc                

المدونات

توصيات الكتب