تحديد ملامح كود بايثون باستخدام timeit وcProfile - KDnuggets

تحديد ملامح كود بايثون باستخدام timeit وcProfile - KDnuggets

عقدة المصدر: 2874649

التنميط رمز بايثون باستخدام timeit وcProfile
صورة المؤلف
 

كمطور برامج، من المحتمل أنك سمعت هذا الاقتباس "التحسين المبكر هو أصل كل الشرور"– أكثر من مرة – في حياتك المهنية. على الرغم من أن التحسين قد لا يكون مفيدًا جدًا (أو ضروريًا للغاية) للمشاريع الصغيرة، إلا أن التنميط غالبًا ما يكون مفيدًا. 

بعد الانتهاء من ترميز الوحدة النمطية، من الممارسات الجيدة أن تقوم بإنشاء ملف تعريف للكود الخاص بك لقياس المدة التي يستغرقها تنفيذ كل قسم. يمكن أن يساعد ذلك في تحديد روائح التعليمات البرمجية وتوجيه التحسينات لتحسين جودة التعليمات البرمجية. لذلك قم دائمًا بتعريف الكود الخاص بك قبل التحسين!

لاتخاذ الخطوات الأولى، سيساعدك هذا الدليل على البدء في إنشاء ملفات التعريف في Python — باستخدام الأداة المضمنة الوقت هو و الملف الشخصي وحدات. ستتعلم كيفية استخدام كل من واجهة سطر الأوامر والعناصر القابلة للاستدعاء المكافئة داخل نصوص Python النصية.

تعد وحدة timeit جزءًا من مكتبة Python القياسية وتوفر بعض الوظائف المريحة التي يمكن استخدامها لتوقيت مقتطفات قصيرة من التعليمات البرمجية.

لنأخذ مثالاً بسيطًا لعكس قائمة بايثون. سنقوم بقياس أوقات التنفيذ للحصول على نسخة معكوسة من القائمة باستخدام:

  • ال reversed() وظيفة، و
  • تقطيع القائمة. 
>>> nums=[6,9,2,3,7]
>>> list(reversed(nums))
[7, 3, 2, 9, 6]
>>> nums[::-1]
[7, 3, 2, 9, 6]

 

تشغيل timeit في سطر الأوامر

يمكنك الجري timeit في سطر الأوامر باستخدام بناء الجملة:

$ python -m timeit -s 'setup-code' -n 'number' -r 'repeat' 'stmt'

 

مطلوب منك تقديم البيان stmt الذي سيتم قياس وقت التنفيذ. 

يمكنك تحديد setup الرمز عند الحاجة - باستخدام الخيار القصير -s أو الخيار الطويل -setup. سيتم تشغيل رمز الإعداد مرة واحدة فقط.

number عدد مرات تشغيل العبارة: الخيار القصير -n أو الخيار الطويل -number اختياري. وعدد مرات تكرار هذه الدورة: الخيار القصير -r أو الخيار الطويل -تكرار اختياري أيضًا.

دعونا نرى ما سبق في العمل على سبيل المثال لدينا:

هنا إنشاء القائمة هو setup الكود وعكس القائمة هو البيان الذي سيتم توقيته:

$ python -m timeit -s 'nums=[6,9,2,3,7]' 'list(reversed(nums))'
500000 loops, best of 5: 695 nsec per loop

 

عندما لا تحدد قيمًا لـ repeat، يتم استخدام القيمة الافتراضية 5. وعندما لا تحدد number، يتم تشغيل الكود عدة مرات حسب الحاجة للوصول إلى إجمالي الوقت 0.2 ثوانٍ على الأقل.

يحدد هذا المثال بوضوح عدد مرات تنفيذ العبارة:

$ python -m timeit -s 'nums=[6,9,2,3,7]' -n 100Bu000 'list(reversed(nums))'
100000 loops, best of 5: 540 nsec per loop

 

القيمة الافتراضية لـ repeat هي 5، ولكن يمكننا ضبطها على أي قيمة مناسبة:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' -r 3 'list(reversed(nums))'
500000 loops, best of 3: 663 nsec per loop

 

دعونا أيضًا نحدد الوقت المناسب لنهج تقسيم القائمة:

$ python3 -m timeit -s 'nums=[6,9,2,3,7]' 'nums[::-1]'
1000000 loops, best of 5: 142 nsec per loop

 

يبدو أن أسلوب تقسيم القائمة أسرع (جميع الأمثلة موجودة في Python 3.10 على Ubuntu 22.04).

تشغيل timeit في برنامج Python النصي

إليك ما يعادل تشغيل timeit داخل برنامج Python النصي:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = timeit.timeit(setup=setup,stmt=stmt1,number=number)
t2 = timeit.timeit(setup=setup,stmt=stmt2,number=number) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

timeit() Callable يُرجع وقت تنفيذ stmt For number من المرات. لاحظ أنه يمكننا أن نذكر بوضوح عدد مرات التشغيل أو التنفيذ number خذ القيمة الافتراضية 1000000.

Output >>
Using reversed() fn.: 0.08982690000000002
Using list slicing: 0.015550800000000004

 

يؤدي هذا إلى تشغيل العبارة — دون تكرار وظيفة المؤقت — للعنصر المحدد number من الأوقات وإرجاع وقت التنفيذ. كما أنه شائع جدًا في الاستخدام time.repeat() وخذ الحد الأدنى من الوقت كما هو موضح:

import timeit setup = 'nums=[9,2,3,7,6]'
number = 100000
stmt1 = 'list(reversed(nums))'
stmt2 = 'nums[::-1]' t1 = min(timeit.repeat(setup=setup,stmt=stmt1,number=number))
t2 = min(timeit.repeat(setup=setup,stmt=stmt2,number=number)) print(f"Using reversed() fn.: {t1}")
print(f"Using list slicing: {t2}")

 

سيؤدي هذا إلى تكرار عملية تشغيل الكود number من المرات repeat عدد المرات وإرجاع الحد الأدنى من وقت التنفيذ. لدينا هنا 5 تكرارات، كل منها 100000 مرة.

Output >>
Using reversed() fn.: 0.055375300000000016
Using list slicing: 0.015101400000000043

لقد رأينا كيف يمكن استخدام الوقت لقياس أوقات تنفيذ مقتطفات التعليمات البرمجية القصيرة. ومع ذلك، من الناحية العملية، من المفيد جدًا إنشاء ملف تعريف لبرنامج Python بأكمله. 

سيعطينا هذا أوقات تنفيذ جميع الوظائف واستدعاءات الأساليب، بما في ذلك الوظائف والأساليب المضمنة. حتى نتمكن من الحصول على فكرة أفضل عن استدعاءات الوظائف الأكثر تكلفة وتحديد فرص التحسين. على سبيل المثال: قد يكون هناك استدعاء API بطيء جدًا. أو قد تحتوي الوظيفة على حلقة يمكن استبدالها بتعبير فهم أكثر بايثونية. 

دعونا نتعلم كيفية إنشاء ملفات تعريف لنصوص Python باستخدام وحدة cProfile (وهي أيضًا جزء من مكتبة Python القياسية). 

خذ بعين الاعتبار نص بايثون التالي:

# main.py
import time def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": func(1000) another_func(20) useful_func([2, 8, 12, 4], 12)

 

لدينا هنا ثلاث وظائف:

  • func() الذي يدور عبر مجموعة من الأرقام ويطبعها.
  • another func() الذي يحتوي على دعوة إلى sleep() وظيفة.
  • useful_func() يقوم بإرجاع فهرس الرقم المستهدف في القائمة (إذا كان الهدف موجودًا في القائمة).

سيتم استدعاء الوظائف المذكورة أعلاه في كل مرة تقوم فيها بتشغيل البرنامج النصي main.py.

تشغيل cProfile في سطر الأوامر

قم بتشغيل cProfile في سطر الأوامر باستخدام:

python3 -m file-name.py

 

لقد قمنا هنا بتسمية الملف main.py:

python3 -m main.py

 

تشغيل هذا يجب أن يعطيك الإخراج التالي:

 Output >> 0 ... 999 Slept for 20 seconds

 

والملف الشخصي التالي:

 

التنميط رمز بايثون باستخدام timeit وcProfile
 

هنا، ncalls يشير إلى عدد المكالمات إلى الوظيفة و percall يشير إلى الوقت لكل استدعاء وظيفة. إذا كانت قيمة ncalls أكبر من واحد إذن percall هو متوسط ​​الوقت عبر كافة المكالمات.

يهيمن على وقت تنفيذ البرنامج النصي another_func الذي يستخدم المدمج في sleep استدعاء الوظيفة (ينام لمدة 20 ثانية). نحن نرى ذلك print استدعاءات الوظائف مكلفة للغاية أيضًا. 

استخدام cProfile في برنامج Python النصي

أثناء تشغيل cProfile في سطر الأوامر يعمل بشكل جيد، يمكنك أيضًا إضافة وظيفة التوصيف إلى برنامج Python النصي. يمكنك استخدام cProfile مقترنًا بـ وحدة pstats للتوصيف والوصول إلى الإحصاءات.

كأفضل ممارسة للتعامل مع إعداد الموارد وتفكيكها بشكل أفضل، استخدم عبارة with وأنشئ كائن ملف تعريف يُستخدم كمدير سياق:

# main.py
import pstats
import time
import cProfile def func(num): for i in range(num): print(i) def another_func(num): time.sleep(num) print(f"Slept for {num} seconds") def useful_func(nums, target): if target in nums: return nums.index(target) if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.print_stats()

 

دعونا نلقي نظرة فاحصة على ملف تعريف الإخراج الذي تم إنشاؤه:

 

التنميط رمز بايثون باستخدام timeit وcProfile
 

عندما تقوم بتنميط برنامج نصي كبير، سيكون من المفيد فرز النتائج حسب وقت التنفيذ. للقيام بذلك يمكنك الاتصال sort_stats على كائن ملف التعريف والفرز بناءً على وقت التنفيذ: 

...
if __name__ == "__main__": with cProfile.Profile() as profile: func(1000) another_func(20) useful_func([2, 8, 12, 4], 12) profile_result = pstats.Stats(profile) profile_result.sort_stats(pstats.SortKey.TIME) profile_result.print_stats()

 

عندما تقوم الآن بتشغيل البرنامج النصي، يجب أن تكون قادرًا على رؤية النتائج مرتبة حسب الوقت:

 

التنميط رمز بايثون باستخدام timeit وcProfile

آمل أن يساعدك هذا الدليل على البدء في إنشاء ملف تعريف في بايثون. تذكر دائمًا أن التحسينات لا ينبغي أن تأتي على حساب سهولة القراءة. إذا كنت مهتمًا بالتعرف على ملفات التعريف الأخرى، بما في ذلك حزم Python التابعة لجهات خارجية، فراجع هذا مقالة عن ملفات تعريف بايثون.
 
 
بالا بريا سي مطور وكاتب تقني من الهند. تحب العمل في تقاطع الرياضيات والبرمجة وعلوم البيانات وإنشاء المحتوى. تشمل مجالات اهتمامها وخبرتها DevOps وعلوم البيانات ومعالجة اللغة الطبيعية. تستمتع بالقراءة والكتابة والترميز والقهوة! تعمل حاليًا على التعلم ومشاركة معرفتها مع مجتمع المطورين من خلال تأليف برامج تعليمية وأدلة إرشادية ومقالات رأي والمزيد.
 

الطابع الزمني:

اكثر من KD nuggets