成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Python 性能分析大全

X_AirDu / 3158人閱讀

摘要:上面手工插斷點(diǎn)的方法十分原始,用起來不是那么方便,即使用了上下文管理器實(shí)現(xiàn)起來還是略顯笨重。它既提供了命令行接口,又能用于代碼文件之中。

雖然運(yùn)行速度慢是 Python 與生俱來的特點(diǎn),大多數(shù)時候我們用 Python 就意味著放棄對性能的追求。但是,就算是用純 Python 完成同一個任務(wù),老手寫出來的代碼可能會比菜鳥寫的代碼塊幾倍,甚至是幾十倍(這里不考慮算法的因素,只考慮語言方面的因素)。很多時候,我們將自己的代碼運(yùn)行緩慢地原因歸結(jié)于python本來就很慢,從而心安理得地放棄深入探究。

但是,事實(shí)真的是這樣嗎?面對python代碼,你有分析下面這些問題嗎:

程序運(yùn)行的速度如何?

程序運(yùn)行時間的瓶頸在哪里?

能否稍加改進(jìn)以提高運(yùn)行速度呢?

為了更好了解python程序,我們需要一套工具,能夠記錄代碼運(yùn)行時間,生成一個性能分析報告,方便徹底了解代碼,從而進(jìn)行針對性的優(yōu)化(本篇側(cè)重于代碼性能分析,不關(guān)注如何優(yōu)化)。

誰快誰慢

假設(shè)有一個字符串,想將里面的空格替換為字符‘-’,用python實(shí)現(xiàn)起來很簡單,下面是四種方案:

def slowest_replace():
    replace_list = []
    for i, char in enumerate(orignal_str):
        c = char if char != " " else "-"
        replace_list.append(c)
    return "".join(replace_list)

def slow_replace():
    replace_str = ""
    for i, char in enumerate(orignal_str):
        c = char if char != " " else "-"
        replace_str += c
    return replace_str

def fast_replace():
    return "-".join(orignal_str.split())

def fastest_replace():
    return orignal_str.replace(" ", "-")

這四種方案的效率如何呢,哪種方案比較慢呢?這是一個問題!

時間斷點(diǎn)

最直接的想法是在開始 replace 函數(shù)之前記錄時間,程序結(jié)束后再記錄時間,計(jì)算時間差即為程序運(yùn)行時間。python提供了模塊 time,其中 time.clock() 在Unix/Linux下返回的是CPU時間(浮點(diǎn)數(shù)表示的秒數(shù)),Win下返回的是以秒為單位的真實(shí)時間(Wall-clock time)。

由于替換函數(shù)耗時可能非常短,所以這里考慮分別執(zhí)行 100000次,然后查看不同函數(shù)的效率。我們的性能分析輔助函數(shù)如下:

def _time_analyze_(func):
    from time import clock
    start = clock()
    for i in range(exec_times):
        func()
    finish = clock()
    print "{:<20}{:10.6} s".format(func.__name__ + ":", finish - start)

這樣就可以了解上面程序的運(yùn)行時間情況:

第一種方案耗時是第四種的 45 倍多,大跌眼鏡了吧!同樣是 python代碼,完成一樣的功能,耗時可以差這么多。

為了避免每次在程序開始、結(jié)束時插入時間斷點(diǎn),然后計(jì)算耗時,可以考慮實(shí)現(xiàn)一個上下文管理器,具體代碼如下:

class Timer(object):
    def __init__(self, verbose=False):
        self.verbose = verbose

    def __enter__(self):
        self.start = clock()
        return self

    def __exit__(self, *args):
        self.end = clock()
        self.secs = self.end - self.start
        self.msecs = self.secs * 1000  # millisecs
        if self.verbose:
            print "elapsed time: %f ms" % self.msecs

使用時只需要將要測量時間的代碼段放進(jìn) with 語句即可,具體的使用例子放在 gist 上。

timeit

上面手工插斷點(diǎn)的方法十分原始,用起來不是那么方便,即使用了上下文管理器實(shí)現(xiàn)起來還是略顯笨重。還好 Python 提供了timeit模塊,用來測試代碼塊的運(yùn)行時間。它既提供了命令行接口,又能用于代碼文件之中。

命令行接口

命令行接口可以像下面這樣使用:

$ python -m timeit -n 1000000 ""I like to reading.".replace(" ", "-")"
1000000 loops, best of 3: 0.253 usec per loop
$ python -m timeit -s "orignal_str = "I like to reading."" ""-".join(orignal_str.split())"
1000000 loops, best of 3: 0.53 usec per loop

具體參數(shù)使用可以用命令 python -m timeit -h 查看幫助。使用較多的是下面的選項(xiàng):

-s S, --setup=S: 用來初始化statement中的變量,只運(yùn)行一次;

-n N, --number=N: 執(zhí)行statement的次數(shù),默認(rèn)會選擇一個合適的數(shù)字;

-r N, --repeat=N: 重復(fù)測試的次數(shù),默認(rèn)為3;

Python 接口

可以用下面的程序測試四種 replace函數(shù)的運(yùn)行情況(完整的測試程序可以在 gist 上找到):

def _timeit_analyze_(func):
    from timeit import Timer
    t1 = Timer("%s()" % func.__name__, "from __main__ import %s" % func.__name__)
    print "{:<20}{:10.6} s".format(func.__name__ + ":", t1.timeit(exec_times))

運(yùn)行結(jié)果如下:

Python的timeit提供了 timeit.Timer() 類,類構(gòu)造方法如下:

Timer(stmt="pass", setup="pass", timer=)

其中:

stmt: 要計(jì)時的語句或者函數(shù);

setup: 為stmt語句構(gòu)建環(huán)境的導(dǎo)入語句;

timer: 基于平臺的時間函數(shù)(timer function);

Timer()類有三個方法:

timeit(number=1000000):?返回stmt執(zhí)行number次的秒數(shù)(float);

repeat(repeat=3, number=1000000): repeat為重復(fù)整個測試的次數(shù),number為執(zhí)行stmt的次數(shù),返回以秒記錄的每個測試循環(huán)的耗時列表;

print_exc(file=None): 打印stmt的跟蹤信息。

此外,timeit 還提供了另外三個函數(shù)方便使用,參數(shù)和 Timer 差不多。

timeit.timeit(stmt="pass", setup="pass", timer=, number=1000000)
timeit.repeat(stmt="pass", setup="pass", timer=, repeat=3, number=1000000)
timeit.default_timer()
profile

以上方法適用于比較簡單的場合,更復(fù)雜的情況下,可以用標(biāo)準(zhǔn)庫里面的profile或者cProfile,它可以統(tǒng)計(jì)程序里每一個函數(shù)的運(yùn)行時間,并且提供了可視化的報表。大多情況下,建議使用cProfile,它是profile的C實(shí)現(xiàn),適用于運(yùn)行時間長的程序。不過有的系統(tǒng)可能不支持cProfile,此時只好用profile。

可以用下面程序測試 timeit_profile() 函數(shù)運(yùn)行時間分配情況。

import cProfile
from time_profile import *

cProfile.run("timeit_profile()")

這樣的輸出可能會很長,很多時候我們感興趣的可能只有耗時最多的幾個函數(shù),這個時候先將cProfile 的輸出保存到診斷文件中,然后用 pstats 定制更加有好的輸出(完整代碼在 gist 上)。

cProfile.run("timeit_profile()", "timeit")
p = pstats.Stats("timeit")
p.sort_stats("time")
p.print_stats(6)

輸出結(jié)果如下:

如果覺得 pstas 使用不方便,還可以使用一些圖形化工具,比如 gprof2dot 來可視化分析 cProfile 的診斷結(jié)果。

vprof

vprof 也是一個不錯的可視化工具,可以用來分析 Python 程序運(yùn)行時間情況。如下圖:

line_profiler

上面的測試最多統(tǒng)計(jì)到函數(shù)的執(zhí)行時間,很多時候我們想知道函數(shù)里面每一行代碼的執(zhí)行效率,這時候就可以用到 line_profiler 了。

line_profiler 的使用特別簡單,在需要監(jiān)控的函數(shù)前面加上 @profile 裝飾器。然后用它提供的 kernprof -l -v [source_code.py] 行進(jìn)行診斷。下面是一個簡單的測試程序 line_profile.py:

from time_profile import slow_replace, slowest_replace

for i in xrange(10000):
    slow_replace()
    slowest_replace()

運(yùn)行后結(jié)果如下:

輸出每列的含義如下:

Line #: 行號

Hits: 當(dāng)前行執(zhí)行的次數(shù).

Time: 當(dāng)前行執(zhí)行耗費(fèi)的時間,單位為 "Timer unit:"

Per Hit: 平均執(zhí)行一次耗費(fèi)的時間.

% Time: 當(dāng)前行執(zhí)行時間占總時間的比例.

Line Contents: 當(dāng)前行的代碼

line_profiler 執(zhí)行時間的估計(jì)不是特別精確,不過可以用來分析當(dāng)前函數(shù)中哪些行是瓶頸。

博客地址

更多閱讀

A guide to analyzing Python performance
timeit – Time the execution of small bits of Python code
Profiling Python using cProfile: a concrete case
profile, cProfile, and pstats – Performance analysis of Python programs.
How can you profile a Python script?
檢測Python程序執(zhí)行效率及內(nèi)存和CPU使用的7種方法
代碼優(yōu)化概要
Python性能優(yōu)化的20條建議

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/38002.html

相關(guān)文章

  • Python各熱門方向常用學(xué)習(xí)、工作網(wǎng)址大全【7000字大總結(jié)】

    摘要:做這一領(lǐng)域的工作,有很多網(wǎng)站能夠起到輔助性的作用。再加上爬蟲相對于其他熱門方向來說,更容易學(xué)。也促使更多人會優(yōu)先選擇學(xué)習(xí)爬蟲。能夠代替手工完成手工無法完成的測試任務(wù),并且可以記錄相關(guān)數(shù)據(jù)及報告。 ...

    linkFly 評論0 收藏0
  • Python入門資料大全(更新ing)

    摘要:在線挑戰(zhàn),還沒用過,貌似現(xiàn)在對英文資料心里還有種抵觸,必須克服實(shí)驗(yàn)樓研發(fā)工程師包含了等學(xué)習(xí)課程。書的作者就是開發(fā)了用于數(shù)據(jù)分析的著名開源庫的作者英文資料,對數(shù)據(jù)分析中要用到的一些庫,等等做了簡要介紹。形式的資料,示例代碼都很全。 showImg(https://segmentfault.com/img/remote/1460000004852849); 一、說明 面對網(wǎng)絡(luò)上紛繁復(fù)雜的資...

    wuaiqiu 評論0 收藏0
  • 后端文章 - 收藏集 - 掘金

    摘要:為什么我會說它們是一樣的簡單思考一下我的后端書架后端掘金我的后端書架月前本書架主要針對后端開發(fā)與架構(gòu)。一方案調(diào)研版本選擇當(dāng)前主流版本是和應(yīng)用的后臺運(yùn)行配置后端掘金醬油一篇,整理一下關(guān)于后臺運(yùn)行的一些配置方式。 分享 50 個完整的 React Native 項(xiàng)目 - 掘金本文為 Marno 原創(chuàng),轉(zhuǎn)載必須保留出處! 公眾號 aMarno,關(guān)注后回復(fù) RN 加入交流群 簡書專題《 Rea...

    CntChen 評論0 收藏0
  • python kotlin 人工智能 資料大全

    摘要:重新定義實(shí)戰(zhàn)鏈接提取碼征服語言基礎(chǔ)與典型應(yīng)用鏈接提取碼算法圖解像小說一樣有趣的算法入門書鏈接提取碼數(shù)據(jù)科學(xué)導(dǎo)論語言實(shí)現(xiàn)鏈接提取碼數(shù)據(jù)結(jié)構(gòu)與算法語言描述裘宗燕編著北京機(jī)械工業(yè)出版社鏈接提取碼深入實(shí)踐陳韶健鏈接提取碼深入淺出鏈接提取碼柯林斯英 重新定義Spring Cloud實(shí)戰(zhàn)鏈接: https://pan.baidu.com/s/1sjl6...提取碼: nn38 征服PYTHON-語...

    dailybird 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<