شگفت انگیز=پایتون=سرعت

کسانی که از پایتون متنفر هستند همیشه می‌گویند یکی از دلایلی که نمی‌خواهند از ان استفاده کنند کند بودن پایتون است.برنامه‌ای که می نویسیم صرف نظر از زبان برنامه نویسی که استفاده کردیم بالا یا پایین بودن سرعت برنامه بستگی مهارت توسعه دهنده و مهارت های او دارد.خوب، بیایید ثابت کنیم که بعضی از افراد اشتباه می‌کنند، ببینیم چطور یک برنامه با کیفت و عملکرد سریع در پایتون درست کنیم.زمانبندی و پروفایلقبل از بهینه سازی کد، ابتدا باید ببینیم که کدام قسمت از کد برنامه،برنامه را کند می کند.گاهی اوقات ممکن است مشخص باشد ولی اگر نمی‌دانید بیاییدچند روش برای پیدا کردن کد بد بهتون بگم.نکته: این برنامه ای که استفاده می‌کنم بر گرفته شده در مستند های پایتون است که عددی را به توان X محاسبه می‌کند# slow_program.pyfrom decimal import *def exp(x):getcontext().prec += 2i, lasts, s, fact, num = 0, 0, 1, 1, 1while s != lasts:lasts = si += 1fact *= inum *= xs += num / factgetcontext().prec -= 2return +sexp(Decimal(150))exp(Decimal(400))exp(Decimal(3000))تنبل ترین روش پروفایلاول از همه و ساده ترین روش برای تنبل ها تایم یونیکس~ $ time python3.8 slow_program.py real 0m11,058s user 0m11,050ssys 0m0,008sاگه برای کل برنامه بخواهید زمان بگذارید کافی نیست....پروفایل دقیق تردر روش دیگر cProfile قرار دارد که اطلاعات بیشتری بهتون می‌دهد~ $ python3.8 -m cProfile -s time slow_program.py 1297 function calls (1272 primitive calls) in 11.081 seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 3 11.079 3.693 11.079 3.693 slow_program.py:4(exp) 1 0.000 0.000 0.002 0.002 {built-in method _imp.create_dynamic} 4/1 0.000 0.000 11.081 11.081 {built-in method builtins.exec} 6 0.000 0.000 0.000 0.000 {built-in method __new__ of type object at 0x9d12c0} 6 0.000 0.000 0.000 0.000 abc.py:132(__new__) 23 0.000 0.000 0.000 0.000 _weakrefset.py:36(__init__) 245 0.000 0.000 0.000 0.000 {built-in method builtins.getattr} 2 0.000 0.000 0.000 0.000 {built-in method marshal.loads} 10 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:1233(find_spec) 8/4 0.000 0.000 0.000 0.000 abc.py:196(__subclasscheck__) 15 0.000 0.000 0.000 0.000 {built-in method posix.stat} 6 0.000 0.000 0.000 0.000 {built-in method builtins.__build_class__} 1 0.000 0.000 0.000 0.000 __init__.py:357(namedtuple) 48 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:57(_path_join) 48 0.000 0.000 0.000 0.000 <frozen importlib._bootstrap_external>:59(<listcomp>) 1 0.000 0.000 11.081 11.081 slow_program.py:1(<module>)در اینجا اسکریپت تست را از ماژول cProfile و ارگومان تایم اجرا کرده ایم. پس زمان بندی انها براساس زمانبندی داخلی است (cumtime). خطوطی که در بالا می‌بینید 10 درصد خروجی واقعی است، همانطور که مشاهده می‌کنیم دستور EXP مقصر اصلی است. با زمانبندی پروفایل می‌توانیم کمی خاص تر به نظر بیاییم.توابع زمانبندی خاصحال که می‌دانیم توجه خود را کجا صرف کنیم، بدون در نظر گرفتن اندازه گیری بقیه کد، عملکرد کند زمان را اندازه بگیرم، برای اینکار از دستور decorator استفاده می‌کنیم:def timeit_wrapper(func): @wraps(func) def wrapper(*args, **kwargs): start = time.perf_counter() # Alternatively, you can use time.process_time() func_return_val = func(*args, **kwargs) end = time.perf_counter() print('{0:<10}.{1:<8} : {2:<8}'.format(func.__module__, func.__name__, end - start)) return func_return_val return wrapperبرای استفاده کردن به مثال زیر توجه کنید:@timeit_wrapperdef exp(x): ...print('{0:<10} {1:<8} {2:^8}'.format('module', 'function', 'time'))exp(Decimal(150))exp(Decimal(400))exp(Decimal(3000))همچین خروجی می‌دهد:~ $ python3.8 slow_program.pymodule function time__main__ .exp : 0.003267502994276583__main__ .exp : 0.038535295985639095__main__ .exp : 11.728486061969306نکته که باید در نظر بگیرم این که کدام زمان را می‌خواهیم اندازه گیری کنیم. بسته تایم این مقدار ها time.perf_counter ، time.process_time برمی‌گرداند. فرق اش در این است که مقدارtime.perf_counter یک مقدار مطلق است که شامل زمان عدم اجرای روند در پایتون هم است. در طرف دیگر time.process_time که زمان کاربر است (به غیر زمان سیستم) که زمان همان پروسه است.سریع تر کنیمبیاید کاری کنیم که برنامه در پایتون سریع تر کار کند. اینجا قرار نیست ترفند خاصی گفته بشه که به طوری شگفت اوری برنامه را سریع تر کند، این بیشتر مربوط به استراتژیهای است که می‌تواند برنامه را تا حدود ۳۰ درصد بهبود بخشید.از داده‌های داخلی استفاده کنیداین کاملا مشهود است که از داده داخلی استفاده کنید بخاطر ساختار پیوندی قوی دارند سرعت بالاتری نیز برخودار می باشد، پس یادبگیریم از ماژول های داخلی استفاده ببریم.ذخیره و یاداوری یا همان کشبرای مثال:import functoolsimport time# caching up to 12 different results@functools.lru_cache(maxsize=12)def slow_func(x): time.sleep(2) # Simulate long computation return xslow_func(1) # ... waiting for 2 sec before getting resultslow_func(1) # already cached - result returned instantaneously!slow_func(3) # ... waiting for 2 sec before getting resultعملکرد فوق با استفاده از محاسبات سنگین time.sleep را شبیه سازی می کند. وقتی برای اولین بار این تابع فراخوانی می شود ۲ ثانیه صبر می‌کند و بعد نتیجه را بازمی گرداند و وقتی دوباره فراخوانی می‌شود نتیجه ذخیره شده است. پس درنتیجه از روی محاسبات سنگین پرش می کند و نتیجه را نمایش می‌دهد.متغییرهای محلیاین بیشتر مربوط به جستجو در متغییر هاست بین متغییر های محلی و سراسری متغییر محلی سرعت بیشتری دارد برای مثال self.name سرعت پایینی دارد و time.time که وارد شده است سرعت اهسته تری دارد.با این روش با کمتر کردن دستورات می توانید به برنامه خود بهبود بخشید# Example #1class FastClass: def do_stuff(self): temp = self.value # this speeds up lookup in loop for i in range(10000): ... # Do something with `temp` here# Example #2import randomdef fast_function(): r = random.random for i in range(10000): print(r()) # calling `r()` here, is faster than global random.random()توابعشاید با حرف های که تا چند دقیقه پیش زدیم خلافش باشد ولی با جدا سازی قطعه های برنامه و توابع در فایل های جداگانه به سرعت برنامه بهبود می بخشید بجای انکه تمامی برنامه ها در یک فایل باشد.def main(): ... # All your previously global codemain()دسترسی به خاصیت هایکی دیگر از راه حل ها برای بهبود برنامه کمتر استفاده کردن از نقطه است. هنگام دسترسی به صفت ها از جستجو در دیکشنری‌ها getattribute استفاده میشود، حال چطوری می‌توانیم کمتر از صفت ها استفاده کنیم.# Slow:import redef slow_func(): for i in range(10000): re.findall(regex, line) # Slow!# Fast:from re import findalldef fast_func(): for i in range(10000): findall(regex, line) # Faster!رشته ها یکی از دیگر موضوع ها استفاده از رشته ها می‌باشد به مثال زیر دقت کنیدf'{s} {t}' # Fast!s + ' ' + t' '.join((s, t))'%s %s' % (s, t)'{} {}'.format(s, t)Template('$s $t').substitute(s=s, t=t) # Slow!جمع بندی امیدوارم مفید واقع شود و تا حد امکان این موارد را در نظر بگیرید.مطالب از این لینک برگفته شده است.