<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          超干分享!如何提高Python的運行速度?

          共 2801字,需瀏覽 6分鐘

           ·

          2020-12-06 19:50

          點擊上方Python知識圈設為星標

          回復100獲取100題PDF



          閱讀文本大概需要 5?分鐘


          近期精彩文章Python100例(附PDF下載地址)

          Python已經(jīng)得到了全球程序員的喜愛,但是還是遭到一些人的詬病,原因之一就是認為它運行緩慢。


          其實某個特定程序(無論使用何種編程語言)的運行速度是快還是慢,在很大程度上取決于編寫該程序的開發(fā)人員自身素質(zhì),以及他們編寫優(yōu)化而高效代碼的能力。


          Medium上一位小哥就詳細講了講如何讓python提速30%,以此證明代碼跑得慢不是python的問題,而是代碼本身的問題。


          01
          時序分析



          在開始進行任何優(yōu)化之前,我們首先需要找出代碼的哪些部分使整個程序變慢。有時程序的問題很明顯,但是如果你一時不知道問題出在哪里,那么這里有一些可能的選項:


          注意:這是我將用于演示的程序,它將進行指數(shù)計算(取自Python文檔):


          # slow_program.py

          from decimal import *
          def?exp(x):
          ????getcontext().prec +=?2
          ????i, lasts, s, fact, num =?0,?0,?1,?1,?1
          ????while?s !=?lasts:
          ????????lasts = s
          ????????i +=?1
          ????????fact *= i
          ????????num *=?x
          ????????s += num / fact
          ????getcontext().prec -=?2
          ????return?+s

          exp(Decimal(150))
          exp(Decimal(400))
          exp(Decimal(3000))


          最簡約的“配置文件”


          首先,最簡單最偷懶的方法——Unix時間命令。


          ~ $ time python3.8?slow_program.py

          real??0m11,058s
          user?0m11,050s
          sys?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?: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?:57(_path_join)
          ???????48????0.000????0.000????0.000????0.000?:59()
          ????????1????0.000????0.000???11.081???11.081?slow_program.py:1()


          在這里,我們使用cProfile模塊和time參數(shù)運行測試腳本,以便按內(nèi)部時間(cumtime)對行進行排序。這給了我們很多信息,你在上面看到的行大約是實際輸出的10%。由此可見,exp函數(shù)是罪魁禍首,現(xiàn)在我們可以更詳細地了解時序和性能分析。


          時序特定功能


          現(xiàn)在我們知道了應當主要關(guān)注哪里,我們可能想對運行速度緩慢的函數(shù)計時,而不用測量其余的代碼。為此,我們可以使用一個簡單的裝飾器:


          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_wrapper

          def?exp(x):
          ????...

          print('{0:<10} {1:<8} {2:^8}'.format('module',?'function',?'time'))
          exp(Decimal(150))
          exp(Decimal(400))
          exp(Decimal(3000))


          這給出我們?nèi)缦螺敵觯?/span>


          ~ $?python3.8?slow_program.py
          module?function???time??
          __main__ .exp??????:?0.003267502994276583
          __main__ .exp??????:?0.038535295985639095
          __main__ .exp??????:?11.728486061969306


          需要考慮的一件事是我們實際想要測量的時間。時間包提供time.perf_countertime.process_time兩個函數(shù)。他們的區(qū)別在于perf_counter返回的絕對值,包括你的Python程序進程未運行時的時間,因此它可能會受到計算機負載的影響。另一方面,process_time僅返回用戶時間(不包括系統(tǒng)時間),這僅是你的過程時間。


          02
          加速吧!



          讓Python程序運行得更快,這部分會很有趣!我不會展示可以解決你的性能問題的技巧和代碼,更多地是關(guān)于構(gòu)想和策略的,這些構(gòu)想和策略在使用時可能會對性能產(chǎn)生巨大影響,在某些情況下,可以將速度提高30%。


          使用內(nèi)置數(shù)據(jù)類型


          這一點很明顯。內(nèi)置數(shù)據(jù)類型非常快,尤其是與我們的自定義類型(例如樹或鏈接列表)相比。這主要是因為內(nèi)置程序是用C實現(xiàn)的,因此在使用Python進行編碼時我們的速度實在無法與之匹敵。


          使用lru_cache緩存/記憶


          我已經(jīng)在上一篇博客中展示了此內(nèi)容,但我認為值得用簡單的示例來重復它:


          import?functools
          import?time
          # caching up to 12 different results
          @functools.lru_cache(maxsize=12)
          def?slow_func(x):
          ????time.sleep(2)?# Simulate long computation
          ????return?x

          slow_func(1)?# ... waiting for 2 sec before getting result
          slow_func(1)?# already cached - result returned instantaneously!
          slow_func(3)?# ... waiting for 2 sec before getting result


          上面的函數(shù)使用time.sleep模擬大量計算。第一次使用參數(shù)1調(diào)用時,它將等待2秒鐘,然后才返回結(jié)果。再次調(diào)用時,結(jié)果已經(jīng)被緩存,因此它將跳過函數(shù)的主體并立即返回結(jié)果。有關(guān)更多實際示例,請參見以前的博客文章。


          使用局部變量


          這與在每個作用域中查找變量的速度有關(guān),因為它不只是使用局部變量還是全局變量。實際上,即使在函數(shù)的局部變量(最快),類級屬性(例如self.name——較慢)和全局(例如,導入的函數(shù))如time.time(最慢)之間,查找速度實際上也有所不同。


          你可以通過使用看似不必要的分配來提高性能,如下所示:


          # Example #1
          class?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 #2
          import?random
          def?fast_function():
          ????r = random.random
          ????for?i?in?range(10000):
          ????????print(r())?# calling `r()` here, is faster than global random.random()


          使用函數(shù)


          這似乎違反直覺,因為調(diào)用函數(shù)會將更多的東西放到堆棧上,并從函數(shù)返回中產(chǎn)生開銷,但這與上一點有關(guān)。如果僅將整個代碼放在一個文件中而不將其放入函數(shù)中,則由于全局變量,它的運行速度會慢得多。因此,你可以通過將整個代碼包裝在main函數(shù)中并調(diào)用一次來加速代碼,如下所示:


          def?main():

          ????...?# All your previously global code

          main()


          不訪問屬性


          可能會使你的程序變慢的另一件事是點運算符(.),它在獲得對象屬性時被使用。此運算符使用__getattribute__觸發(fā)字典查找,這會在代碼中產(chǎn)生額外的開銷。那么,我們?nèi)绾尾拍苷嬲苊猓ㄏ拗疲┦褂盟兀?/span>


          # Slow:
          import?re
          def?slow_func():
          ????for?i?in?range(10000):
          ????????re.findall(regex, line)?# Slow!

          # Fast:
          from?re?import?findall
          def?fast_func():
          ????for?i?in?range(10000):
          ????????findall(regex, line)?# Faster!


          當心字符串


          使用模數(shù)(%s).format()進行循環(huán)運行時,字符串操作可能會變得非常慢。我們有什么更好的選擇?根據(jù)雷蒙德·海廷格(Raymond Hettinger)最近的推特,我們唯一應該使用的是f-string,它是最易讀,最簡潔且最快的方法。根據(jù)該推特,這是你可以使用的方法列表——最快到最慢:


          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!


          生成器本質(zhì)上并沒有更快,因為它們被允許進行延遲計算,從而節(jié)省了內(nèi)存而不是時間。但是,保存的內(nèi)存可能會導致你的程序?qū)嶋H運行得更快。這是怎么做到的?如果你有一個很大的數(shù)據(jù)集,而沒有使用生成器(迭代器),那么數(shù)據(jù)可能會溢出CPU L1緩存,這將大大減慢內(nèi)存中值的查找速度。


          在性能方面,非常重要的一點是CPU可以將正在處理的所有數(shù)據(jù)盡可能地保存在緩存中。你可以觀看Raymond Hettingers的視頻,他在其中提到了這些問題。

          03
          結(jié)論



          優(yōu)化的首要規(guī)則是不要優(yōu)化。但是,如果確實需要,那么我希望上面這些技巧可以幫助你。但是,在優(yōu)化代碼時要小心,因為它可能最終使你的代碼難以閱讀,因此難以維護,這可能超過優(yōu)化的好處。


          原文鏈接:

          https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32

          pk哥個人微信


          添加pk哥個人微信即送Python資料


          → Python知識點100題的PDF

          → Python相關(guān)的電子書10本


          記得備注:“100題”




          往期推薦
          01

          公眾號所有文章匯總導航(2-10更新)

          02

          Python100例(附PDF下載地址)

          03

          求你了,別再用 pip 那烏龜?shù)乃俣热グ惭b庫了!


          點擊閱讀原文查看pk哥原創(chuàng)視頻

          我就知道你“在看”

          瀏覽 69
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  特级丰满少妇一级AAAA爱毛片 | 国产无码中文字幕在线 | 亚洲精品国产AV | 51无码人妻精品1国产蜜芽 | 波多野结衣视频在线观看 |