مقدمة لتحسين الأداء في Go
Go ، أو Golang ، هي لغة برمجة حديثة ومفتوحة المصدر تم تطويرها في Google بواسطة Robert Griesemer و Rob Pike و Ken Thompson. يتميز Go بخصائص أداء ممتازة ، وذلك بفضل بساطته وكتابته القوية ودعم التزامن المدمج وجمع البيانات المهملة. يختار المطورون Go نظرًا لسرعته وقدرته على التوسع وسهولة الصيانة عند إنشاء تطبيقات من جانب الخادم وخطوط أنابيب البيانات وأنظمة أخرى عالية الأداء.
لتحقيق أقصى قدر من الأداء من تطبيقات Go ، قد ترغب في تحسين التعليمات البرمجية الخاصة بك. يتطلب ذلك فهم معوقات الأداء وإدارة تخصيص الذاكرة بكفاءة والاستفادة من التزامن. إحدى الحالات الجديرة بالملاحظة لاستخدام Go في التطبيقات ذات الأداء الحرج هي AppMaster - وهو نظام أساسي قوي لا يحتوي على تعليمات برمجية لإنشاء تطبيقات الخلفية والويب والهاتف المحمول. ينشئ AppMaster تطبيقاته الخلفية باستخدام Go ، مما يضمن قابلية التوسع والأداء العالي المطلوب لحالات الاستخدام عالية التحميل والمؤسسة. في هذه المقالة ، سنغطي بعض تقنيات التحسين الأساسية ، بدءًا من الاستفادة من دعم Go's التزامن.
الاستفادة من التزامن لتحسين الأداء
يتيح التزامن تشغيل مهام متعددة بشكل متزامن ، والاستفادة المثلى من موارد النظام المتاحة وتحسين الأداء. تم تصميم Go مع وضع التزامن في الاعتبار ، مما يوفر Goroutines والقنوات كبنيات لغة مضمنة لتبسيط المعالجة المتزامنة.
جوروتين
Goroutines هي خيوط خفيفة الوزن يديرها وقت تشغيل Go. يعد إنشاء Goroutine أمرًا بسيطًا - ما عليك سوى استخدام الكلمة الرئيسية `` go '' قبل استدعاء الوظيفة: `` go go go funcName () '' عندما يبدأ Goroutine في العمل ، فإنه يشترك في نفس مساحة العنوان مثل Goroutines الأخرى. هذا يجعل التواصل بين Goroutines سهلاً. ومع ذلك ، يجب أن تحرص على الوصول إلى الذاكرة المشتركة لمنع سباقات البيانات.
القنوات
القنوات هي الشكل الأساسي للاتصال بين Goroutines في Go. القناة عبارة عن قناة مكتوبة يمكنك من خلالها إرسال واستقبال القيم بين Goroutines. لإنشاء قناة ، استخدم الكلمة الأساسية `chan`:` `go channelName: = make (chan dataType)` `يتم إرسال واستقبال القيم عبر قناة باستخدام عامل تشغيل السهم (` <-`). إليك مثال: `` go // إرسال قيمة إلى قناة اسم القناة <- valueToSend // تلقي قيمة من قناة مستلمة القيمة: = <-channelName "" استخدام القنوات بشكل صحيح يضمن الاتصال الآمن بين Goroutines ويزيل ظروف السباق المحتملة .
تنفيذ أنماط التزامن
يتيح تطبيق أنماط التزامن ، مثل التوازي وخطوط الأنابيب والمروحة في / توزيع الهواء ، لمطوري Go إنشاء تطبيقات عالية الأداء . فيما يلي شرح موجز لهذه الأنماط:
- التوازي: قسّم العمليات الحسابية إلى مهام أصغر وتنفيذ هذه المهام بشكل متزامن للاستفادة من نوى المعالج المتعددة وتسريع العمليات الحسابية.
- خطوط الأنابيب: تنظيم سلسلة من الوظائف إلى مراحل ، حيث تعالج كل مرحلة البيانات وتمررها إلى المرحلة التالية عبر قناة. يؤدي ذلك إلى إنشاء خط أنابيب معالجة حيث تعمل المراحل المختلفة بشكل متزامن لمعالجة البيانات بكفاءة.
- Fan-In / Fan-Out: قم بتوزيع مهمة عبر Goroutines المتعددة (fan-out) ، والتي تعالج البيانات بشكل متزامن. بعد ذلك ، قم بتجميع النتائج من Goroutines في قناة واحدة (fan-in) لمزيد من المعالجة أو التجميع. عند تنفيذها بشكل صحيح ، يمكن لهذه الأنماط تحسين أداء تطبيق Go وقابلية التوسع بشكل كبير.
تطبيقات التنميط Go للتحسين
التنميط هو عملية تحليل التعليمات البرمجية لتحديد الاختناقات في الأداء وعدم كفاءة استهلاك الموارد. يوفر Go أدوات مضمنة ، مثل حزمة "pprof" ، والتي تسمح للمطورين بتوصيف تطبيقاتهم وفهم خصائص الأداء. من خلال تحديد سمات كود Go الخاص بك ، يمكنك تحديد فرص التحسين وضمان الاستخدام الفعال للموارد.
تشكيل وحدة المعالجة المركزية
يقيس تعريف وحدة المعالجة المركزية أداء تطبيق Go الخاص بك من حيث استخدام وحدة المعالجة المركزية. يمكن لحزمة "pprof" إنشاء ملفات تعريف وحدة المعالجة المركزية التي توضح المكان الذي يقضي فيه تطبيقك معظم وقت تنفيذه. لتمكين تعريف وحدة المعالجة المركزية ، استخدم مقتطف الشفرة التالي: `` go import "runtime / pprof" // ... func main () {// أنشئ ملفًا لتخزين ملف تعريف وحدة المعالجة المركزية f ، err: = os.Create ( "cpu_profile.prof") إذا أخطأ! err! = لا شيء {log.Fatal (err)} defer pprof.StopCPUProfile () // قم بتشغيل رمز التطبيق الخاص بك هنا} `` بعد تشغيل التطبيق الخاص بك ، سيكون لديك ملف `cpu_profile.prof` يمكنك تحليله باستخدام أدوات "pprof" أو تصور بمساعدة ملف تعريف متوافق.
التنميط الذاكرة
يركز تحديد سمات الذاكرة على تخصيص ذاكرة تطبيق Go واستخدامها ، مما يساعدك على تحديد التسريبات المحتملة للذاكرة أو التخصيص المفرط أو المناطق التي يمكن تحسين الذاكرة فيها. لتمكين تصنيف الذاكرة ، استخدم مقتطف الشفرة هذا: `` go import "runtime / pprof" // ... func main () {// قم بتشغيل كود التطبيق الخاص بك هنا // إنشاء ملف لتخزين ملف تعريف الذاكرة f ، err : = os. إحصائيات دقيقة للذاكرة إذا أخطأت: = pprof.WriteHeapProfile (f) ؛ err! = nil {log.Fatal (err)}} `` على غرار ملف تعريف وحدة المعالجة المركزية ، يمكنك تحليل ملف `mem_profile.prof` باستخدام أدوات` pprof` أو تصور ذلك باستخدام ملف تعريف متوافق.
من خلال الاستفادة من إمكانات ملفات تعريف Go ، يمكنك الحصول على رؤى حول أداء تطبيقك وتحديد مجالات التحسين. يساعدك هذا في إنشاء تطبيقات فعالة وعالية الأداء تتوسع بشكل فعال وتدير الموارد على النحو الأمثل.
تخصيص الذاكرة والمؤشرات في Go
يمكن أن يكون لتحسين تخصيص الذاكرة في Go تأثير كبير على أداء تطبيقك. تقلل الإدارة الفعالة للذاكرة من استخدام الموارد ، وتسرع من أوقات التنفيذ ، وتقلل من عبء جمع البيانات المهملة. في هذا القسم ، سنناقش استراتيجيات تعظيم استخدام الذاكرة والعمل بأمان مع المؤشرات.
أعد استخدام الذاكرة حيثما أمكن ذلك
تتمثل إحدى الطرق الأساسية لتحسين تخصيص الذاكرة في Go في إعادة استخدام الكائنات كلما أمكن ذلك بدلاً من التخلص منها وتخصيص كائنات جديدة. يستخدم Go مجموعة القمامة لإدارة الذاكرة ، لذلك في كل مرة تقوم فيها بإنشاء كائنات وتجاهلها ، يتعين على جامع القمامة التنظيف بعد تطبيقك. يمكن أن يؤدي ذلك إلى زيادة الأداء ، خاصة للتطبيقات عالية الإنتاجية.
ضع في اعتبارك استخدام مجموعات الكائنات ، مثل sync.Pool أو التنفيذ المخصص ، لإعادة استخدام الذاكرة بشكل فعال. تقوم تجمعات الكائنات بتخزين وإدارة مجموعة من الكائنات التي يمكن إعادة استخدامها بواسطة التطبيق. من خلال إعادة استخدام الذاكرة من خلال مجموعات الكائنات ، يمكنك تقليل المقدار الإجمالي لتخصيص الذاكرة وإلغاء تخصيصها ، مما يقلل من تأثير جمع البيانات المهملة على أداء التطبيق الخاص بك.
تجنب المخصصات غير الضرورية
يساعد تجنب المخصصات غير الضرورية في تقليل ضغط جامع القمامة. بدلاً من إنشاء كائنات مؤقتة ، استخدم هياكل أو شرائح البيانات الموجودة. يمكن تحقيق ذلك من خلال:
- التخصيص المسبق للشرائح ذات الحجم المعروف باستخدام
make([]T, size, capacity)
. - استخدام وظيفة
append
بحكمة لتجنب إنشاء شرائح وسيطة أثناء التسلسل. - تجنب تمرير الهياكل الكبيرة من حيث القيمة ؛ بدلاً من ذلك ، استخدم المؤشرات لتمرير مرجع إلى البيانات.
مصدر شائع آخر لتخصيص الذاكرة غير الضروري هو استخدام الإغلاق. على الرغم من أن عمليات الإغلاق مريحة ، إلا أنها يمكن أن تؤدي إلى عمليات تخصيص إضافية. كلما كان ذلك ممكنًا ، قم بتمرير معلمات الوظيفة بشكل صريح بدلاً من التقاطها من خلال عمليات الإغلاق.
العمل بأمان مع المؤشرات
المؤشرات هي تركيبات قوية في Go ، مما يسمح للكود الخاص بك بالإشارة إلى عناوين الذاكرة مباشرة. ومع ذلك ، مع هذه القوة ، تأتي إمكانية حدوث أخطاء متعلقة بالذاكرة ومشكلات في الأداء. للعمل بأمان مع المؤشرات ، اتبع أفضل الممارسات التالية:
- استخدم المؤشرات باعتدال وعند الضرورة فقط. قد يؤدي الاستخدام المفرط إلى إبطاء التنفيذ وزيادة استهلاك الذاكرة.
- حافظ على نطاق استخدام المؤشر في حده الأدنى. كلما كان النطاق أكبر ، كان من الصعب تتبع المرجع وتجنب تسرب الذاكرة.
- تجنب
unsafe.Pointer
ما لم يكن ضروريًا للغاية ، لأنه يتجاوز أمان نوع Go ويمكن أن يؤدي إلى مشكلات يصعب تصحيحها. - استخدم الحزمة
sync/atomic
للعمليات الذرية على الذاكرة المشتركة. عمليات المؤشر العادية ليست ذرية ويمكن أن تؤدي إلى سباقات البيانات إذا لم تتم مزامنتها باستخدام الأقفال أو آليات المزامنة الأخرى.
قياس الأداء الخاص بك تطبيقات Go
المقارنة المعيارية هي عملية قياس وتقييم أداء تطبيق Go الخاص بك في ظل ظروف مختلفة. يساعدك فهم سلوك التطبيق الخاص بك في ظل أعباء العمل المختلفة على تحديد الاختناقات وتحسين الأداء والتحقق من أن التحديثات لا تقدم تراجعًا في الأداء.
يحتوي Go على دعم مدمج لقياس الأداء ، يتم توفيره من خلال حزمة testing
. يمكّنك من كتابة اختبارات معيارية تقيس أداء وقت تشغيل الكود الخاص بك. يتم استخدام أمر go test
المدمج لتشغيل الاختبارات ، والتي تُخرج النتائج بتنسيق موحد.
كتابة الاختبارات المعيارية
يتم تعريف وظيفة قياس الأداء بشكل مشابه لوظيفة الاختبار ، ولكن بتوقيع مختلف:
func BenchmarkMyFunction(b *testing.B) { // Benchmarking code goes here... }
يحتوي الكائن *testing.B
الذي تم تمريره إلى الوظيفة على العديد من الخصائص والطرق المفيدة للقياس:
-
bN
: عدد التكرارات التي يجب تشغيل وظيفة قياس الأداء. -
b.ReportAllocs()
: يسجل عدد عمليات تخصيص الذاكرة أثناء الاختبار. -
b.SetBytes(int64)
: يضبط عدد وحدات البايت التي تتم معالجتها لكل عملية ، وتستخدم لحساب الإنتاجية.
قد يتضمن اختبار قياس الأداء النموذجي الخطوات التالية:
- قم بإعداد البيئة اللازمة وإدخال البيانات للوظيفة التي يتم قياسها.
- أعد ضبط المؤقت (
b.ResetTimer()
) لإزالة أي وقت إعداد من قياسات المعيار. - التكرار خلال المعيار مع العدد المحدد من التكرارات:
for i := 0; i < bN; i++
. - تنفيذ الوظيفة التي يتم قياسها مع بيانات الإدخال المناسبة.
إجراء اختبارات المعيار
قم بتشغيل اختبارات قياس الأداء باستخدام الأمر go test
، بما في ذلك علامة -bench
متبوعة بتعبير عادي يطابق وظائف قياس الأداء التي تريد تشغيلها. على سبيل المثال:
go test -bench=.
يقوم هذا الأمر بتشغيل جميع وظائف قياس الأداء في الحزمة الخاصة بك. لتشغيل معيار معيّن ، قم بتوفير تعبير عادي يطابق اسمه. يتم عرض نتائج المقارنة المعيارية بتنسيق جدولي ، مع إظهار اسم الوظيفة وعدد التكرارات والوقت لكل عملية وتخصيصات الذاكرة إذا تم تسجيلها.
تحليل نتائج المعيار
قم بتحليل نتائج الاختبارات المعيارية لفهم خصائص أداء تطبيقك وتحديد مجالات التحسين. قارن أداء التطبيقات أو الخوارزميات المختلفة ، وقياس تأثير التحسينات ، واكتشف انحدارات الأداء عند تحديث الكود.
نصائح إضافية لتحسين أداء Go
بالإضافة إلى تحسين تخصيص الذاكرة وقياس أداء تطبيقاتك ، إليك بعض النصائح الأخرى لتحسين أداء برامج Go:
- تحديث إصدار Go الخاص بك : استخدم دائمًا أحدث إصدار من Go ، حيث يتضمن غالبًا تحسينات وتحسينات في الأداء.
- الوظائف المضمنة عند الاقتضاء : يمكن أن يساعد تضمين الوظيفة في تقليل عبء استدعاء الوظيفة ، وتحسين الأداء. استخدم
go build -gcflags '-l=4'
للتحكم في العدوانية الداخلية (القيم الأعلى تزيد من التداخل). - استخدام القنوات المخزنة : عند العمل مع التزامن واستخدام قنوات الاتصال ، استخدم القنوات المخزنة لمنع الحجب وتحسين الإنتاجية.
- اختر هياكل البيانات الصحيحة : حدد أنسب بنية بيانات لاحتياجات تطبيقك. يمكن أن يتضمن ذلك استخدام الشرائح بدلاً من المصفوفات عندما يكون ذلك ممكنًا ، أو استخدام الخرائط والمجموعات المضمنة لعمليات البحث والمعالجات الفعالة.
- حسِّن الكود الخاص بك - قليلاً في كل مرة : ركز على تحسين منطقة واحدة في كل مرة ، بدلاً من محاولة معالجة كل شيء في وقت واحد. ابدأ بمعالجة أوجه القصور الخوارزمية ، ثم انتقل إلى إدارة الذاكرة والتحسينات الأخرى.
يمكن أن يكون لتطبيق تقنيات تحسين الأداء هذه في تطبيقات Go تأثير عميق على قابلية التوسع ، واستخدام الموارد ، والأداء العام. من خلال الاستفادة من قوة أدوات Go المضمنة والمعرفة المتعمقة التي تمت مشاركتها في هذه المقالة ، ستكون مجهزًا جيدًا لتطوير تطبيقات عالية الأداء يمكنها التعامل مع أعباء العمل المتنوعة.
هل تتطلع إلى تطوير تطبيقات خلفية قابلة للتطوير وفعالة باستخدام Go؟ ضع في اعتبارك AppMaster ، وهو نظام أساسي قوي لا يحتوي على تعليمات برمجية ينشئ تطبيقات خلفية باستخدام Go (Golang) للحصول على أداء عالٍ وقابلية تطوير مذهلة ، مما يجعله خيارًا مثاليًا لحالات الاستخدام عالية التحميل والشركات. تعرف على المزيد حول AppMaster وكيف يمكن أن تحدث ثورة في عملية التطوير الخاصة بك.