python decimal_【进阶】嫌弃Python慢,试试这几个方法?
生活随笔
收集整理的這篇文章主要介紹了
python decimal_【进阶】嫌弃Python慢,试试这几个方法?
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
(給機器學習算法與Python學習加星標,提升AI技能)
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 命令:~?$?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 參數運行測試腳本,使輸出行按照內部時間(cumtime)排序。這給我們提供了大量信息,上面你看到的行只是實際輸出的 10%。從輸出結果我們可以看到 exp 函數是罪魁禍首(驚不驚喜,意不意外),現在我們可以更加專注于計時和性能分析了……計時專用函數現在我們知道了需要關注哪里,那么我們可能只想要給運行緩慢的函數計時而不去管代碼的其他部分。我們可以使用一個簡單的裝飾器來做到這點: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))得到如下輸出:~?$?python3.8?slow_program.py
module?????function???time??
__main__??.exp??????:?0.003267502994276583
__main__??.exp??????:?0.038535295985639095
__main__??.exp??????:?11.728486061969306此時我們需要考慮想要測量哪一類時間。time 庫提供了 time.perf_counter 和 time.process_time 兩種時間。其區別在于,perf_counter 返回絕對值,其中包括了 Python 程序并不在運行的時間,因此它可能受到機器負載的影響。而 process_time 只返回用戶時間(除去了系統時間),也就是只有進程運行時間。讓程序更快現在到了真正有趣的部分了,讓 Python 程序跑得更快!我不會告訴你一些奇技淫巧或代碼段來神奇地解決程序的性能問題,而更多是關于通用的想法和策略。使用這些策略,可以對程序性能產生巨大的影響,有時甚至可以帶來高達 30% 的提速。使用內置的數據類型這一點非常明顯。內置的數據類型非常快,尤其相比于樹或鏈表等自定義類型而言。這主要是因為內置數據類型使用 C 語言實現,使用 Python 實現的代碼在運行速度上和它們沒法比。使用 lru_cache 實現緩存/記憶我在之前的博客中介紹過這一技巧,但我認為它值得用一個簡單例子再次進行說明: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上面的函數使用 time.sleep 模擬了繁重的計算過程。當我們第一次使用參數 1 調用函數時,它等待了 2 秒鐘后返回了結果。當再次調用時,結果已經被緩存起來,所以它跳過了函數體,直接返回結果。使用局部變量這和每個作用域中變量的查找速度有關。我之所以說「每個作用域」,是因為這不僅僅關乎局部變量或全局變量。事實上,就連函數中的局部變量、類級別的屬性和全局導入函數這三者的查找速度都會有區別。函數中的局部變量最快,類級別屬性(如 self.name)慢一些,全局導入函數(如 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()
????...??#?All?your?previously?global?code
main()不要訪問屬性另一個可能讓程序變慢的東西是用來訪問對象屬性的點運算符(.)。這個運算符會引起程序使用__getattribute__進行字典查找,進而為程序帶來不必要的開銷。那么,我們怎么避免(或者限制)使用它呢?#??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!當心字符串當在循環中使用取模運算符(%s)或 .format() 時,字符串操作會變得很慢。有沒有更好的選擇呢?根據 Raymond Hettinger 近期發布的推文,我們只需要使用 f-string 即可,它可讀性更強,代碼更加緊湊,并且速度更快!基于這一觀點,如下從快到慢列出了你可以使用的一系列方法: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!生成器本質上并不會更快,因為它們的目的是惰性計算,以節省內存而非節省時間。然而,節省的內存會讓程序運行更快。為什么呢?如果你有一個大型數據集,并且你沒有使用生成器(迭代器),那么數據可能造成 CPU 的 L1 緩存溢出,進而導致訪存速度顯著變慢。當涉及到效率時,非常重要的一點是 CPU 會將它正在處理的數據保存得離自己越近越好,也就是保存在緩存中。讀者可以看一看 Raymond Hettingers 的演講(https://www.youtube.com/watch?v=OSGv2VnC0go&t=8m17s),其中提到了這些問題。總結優化的第一要義就是「不要去做」。但如果你必須要做,我希望這些小技巧可以幫助到你。然而,優化代碼時一定要謹慎,因為該操作可能最終造成代碼可讀性變差、可維護性變差,這些弊端可能超過代碼優化所帶來的好處。參考鏈接:https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32
選自towardsdatascience,作者:Martin Heinz
本文轉自機器之心(nearhuman2014)
本文將介紹如何提升 Python 程序的效率,讓它們運行飛快!
計時與性能分析在開始優化之前,我們首先需要找到代碼的哪一部分真正拖慢了整個程序。有時程序性能的瓶頸顯而易見,但當你不知道瓶頸在何處時,這里有一些幫助找到性能瓶頸的辦法:注:下列程序用作演示目的,該程序計算 e 的 X 次方(摘自 Python 文檔):#?slow_program.pyfrom?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 命令:~?$?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 參數運行測試腳本,使輸出行按照內部時間(cumtime)排序。這給我們提供了大量信息,上面你看到的行只是實際輸出的 10%。從輸出結果我們可以看到 exp 函數是罪魁禍首(驚不驚喜,意不意外),現在我們可以更加專注于計時和性能分析了……計時專用函數現在我們知道了需要關注哪里,那么我們可能只想要給運行緩慢的函數計時而不去管代碼的其他部分。我們可以使用一個簡單的裝飾器來做到這點: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))得到如下輸出:~?$?python3.8?slow_program.py
module?????function???time??
__main__??.exp??????:?0.003267502994276583
__main__??.exp??????:?0.038535295985639095
__main__??.exp??????:?11.728486061969306此時我們需要考慮想要測量哪一類時間。time 庫提供了 time.perf_counter 和 time.process_time 兩種時間。其區別在于,perf_counter 返回絕對值,其中包括了 Python 程序并不在運行的時間,因此它可能受到機器負載的影響。而 process_time 只返回用戶時間(除去了系統時間),也就是只有進程運行時間。讓程序更快現在到了真正有趣的部分了,讓 Python 程序跑得更快!我不會告訴你一些奇技淫巧或代碼段來神奇地解決程序的性能問題,而更多是關于通用的想法和策略。使用這些策略,可以對程序性能產生巨大的影響,有時甚至可以帶來高達 30% 的提速。使用內置的數據類型這一點非常明顯。內置的數據類型非常快,尤其相比于樹或鏈表等自定義類型而言。這主要是因為內置數據類型使用 C 語言實現,使用 Python 實現的代碼在運行速度上和它們沒法比。使用 lru_cache 實現緩存/記憶我在之前的博客中介紹過這一技巧,但我認為它值得用一個簡單例子再次進行說明: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上面的函數使用 time.sleep 模擬了繁重的計算過程。當我們第一次使用參數 1 調用函數時,它等待了 2 秒鐘后返回了結果。當再次調用時,結果已經被緩存起來,所以它跳過了函數體,直接返回結果。使用局部變量這和每個作用域中變量的查找速度有關。我之所以說「每個作用域」,是因為這不僅僅關乎局部變量或全局變量。事實上,就連函數中的局部變量、類級別的屬性和全局導入函數這三者的查找速度都會有區別。函數中的局部變量最快,類級別屬性(如 self.name)慢一些,全局導入函數(如 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()
使用函數
這也許有些反直覺,因為調用函數會讓更多的東西入棧,進而在函數返回時為程序帶來負擔,但這其實和之前的策略相關。如果你只是把所有代碼扔進一個文件而沒有把它們放進函數,那么它會因為眾多的全局變量而變慢。因此,你可以通過將所有代碼封裝在 main 函數中并調用它來實現加速,如下所示:def?main():????...??#?All?your?previously?global?code
main()不要訪問屬性另一個可能讓程序變慢的東西是用來訪問對象屬性的點運算符(.)。這個運算符會引起程序使用__getattribute__進行字典查找,進而為程序帶來不必要的開銷。那么,我們怎么避免(或者限制)使用它呢?#??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!當心字符串當在循環中使用取模運算符(%s)或 .format() 時,字符串操作會變得很慢。有沒有更好的選擇呢?根據 Raymond Hettinger 近期發布的推文,我們只需要使用 f-string 即可,它可讀性更強,代碼更加緊湊,并且速度更快!基于這一觀點,如下從快到慢列出了你可以使用的一系列方法: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!生成器本質上并不會更快,因為它們的目的是惰性計算,以節省內存而非節省時間。然而,節省的內存會讓程序運行更快。為什么呢?如果你有一個大型數據集,并且你沒有使用生成器(迭代器),那么數據可能造成 CPU 的 L1 緩存溢出,進而導致訪存速度顯著變慢。當涉及到效率時,非常重要的一點是 CPU 會將它正在處理的數據保存得離自己越近越好,也就是保存在緩存中。讀者可以看一看 Raymond Hettingers 的演講(https://www.youtube.com/watch?v=OSGv2VnC0go&t=8m17s),其中提到了這些問題。總結優化的第一要義就是「不要去做」。但如果你必須要做,我希望這些小技巧可以幫助到你。然而,優化代碼時一定要謹慎,因為該操作可能最終造成代碼可讀性變差、可維護性變差,這些弊端可能超過代碼優化所帶來的好處。參考鏈接:https://towardsdatascience.com/making-python-programs-blazingly-fast-c1cd79bd1b32
本文為機器之心編譯,轉載請聯系本公眾號獲得授權。
推薦閱讀
驚呆了!這樣可以將Numpy加速700倍!
神經網絡訓不好,看看這37個坑!
InfoSpider | 這個開源爬蟲工具箱,一不小心火了!
從AlexNet到BERT:深度學習中那些最重要idea的最簡單回顧
總結
以上是生活随笔為你收集整理的python decimal_【进阶】嫌弃Python慢,试试这几个方法?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何获取投票提交地址_简单实用 | 20
- 下一篇: python爬取新浪新闻首页_学习了《p