<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # Profiling > 原文: [http://docs.cython.org/en/latest/src/tutorial/profiling_tutorial.html](http://docs.cython.org/en/latest/src/tutorial/profiling_tutorial.html) 這部分描述了 Cython 的性能分析能力。如果您熟悉純 Python 代碼的分析,則只能閱讀第一部分( [Cython Profiling Basics](#profiling-basics))。如果您不熟悉 Python 分析,您還應該閱讀教程( [分析教程](#profiling-tutorial) ),它將逐步引導您完成一個完整的示例。 ## Cython Profiling Basics Cython 中的分析由編譯器指令控制。它可以通過 Cython 裝飾器設置為整個文件或基于每個功能。 ### 啟用完整源文件的分析 通過全局指令將完整源文件的分析啟用到文件頂部的 Cython 編譯器: ```py # cython: profile=True ``` 請注意,分析會給每個函數調用帶來輕微的開銷,因此會使程序變慢(或者很多,如果你經常調用一些小函數)。 啟用后,從 cProfile 模塊調用時,您的 Cython 代碼將像 Python 代碼一樣運行。這意味著您可以使用與僅 Python 代碼相同的工具,將您的 Cython 代碼與 Python 代碼一起分析。 ### 禁用分析功能 如果您的分析因為某些小功能的調用開銷而變得混亂,而您希望在您的配置文件中看不到這些功能 - 無論是因為您計劃內聯它們還是因為您確定不能讓它們更快 - 你可以使用一個特殊的裝飾器來禁用一個函數的分析(無論它是否全局啟用): ```py cimport cython @cython.profile(False) def my_often_called_function(): pass ``` ### 啟用行跟蹤 要獲得更詳細的跟蹤信息(對于可以使用它的工具),您可以啟用行跟蹤: ```py # cython: linetrace=True ``` 這也將啟用分析支持,因此不需要上面的`profile=True`選項。例如,覆蓋率分析需要線跟蹤。 請注意,即使通過編譯器指令啟用了行跟蹤,默認情況下也不會使用它。由于運行時減速可能很大,因此必須由 C 編譯器通過設置 C 宏定義`CYTHON_TRACE=1`進行編譯。要在跟蹤中包含 nogil 函數,請設置`CYTHON_TRACE_NOGIL=1`(表示`CYTHON_TRACE=1`)。可以在`setup.py`腳本的擴展定義中定義 C 宏,也可以使用以下文件頭注釋設置源文件中的相應 distutils 選項(如果`cythonize()`用于編譯): ```py # distutils: define_macros=CYTHON_TRACE_NOGIL=1 ``` ### 啟用覆蓋率分析 從 Cython 0.23 開始,線跟蹤(見上文)也支持使用 [coverage.py](https://coverage.readthedocs.io/) 工具報告覆蓋率報告。要使覆蓋率分析了解 Cython 模塊,還需要在`.coveragerc`文件中啟用 Cython 的 coverage 插件,如下所示: ```py [run] plugins = Cython.Coverage ``` 使用此插件,您的 Cython 源文件應該在 coverage 報告中正常顯示。 要將覆蓋率報告包含在 Cython 帶注釋的 HTML 文件中,您需要首先運行 coverage.py 工具以生成 XML 結果文件。將此文件傳遞到`cython`命令,如下所示: ```py $ cython --annotate-coverage coverage.xml package/mymodule.pyx ``` 這將重新編譯 Cython 模塊并在其處理的每個 Cython 源文件旁邊生成一個 HTML 輸出文件,其中包含 coverage 報告中包含的行的顏色標記。 ## 分析教程 這將是一個完整的教程,從頭到尾,分析 Python 代碼,將其轉換為 Cython 代碼并保持分析直到它足夠快。 作為一個玩具示例,我們想要評估平方倒數的總和,直到某個整數![n](https://img.kancloud.cn/a2/55/a255fb6a8d1b4f305135147af8b00a70_9x7.jpg)來評估![\pi](https://img.kancloud.cn/85/66/8566b582f40fed3aac17eaf207610774_9x7.jpg)。我們想要使用的關系已經由歐拉在 1735 年證明并被稱為[巴塞爾問題](https://en.wikipedia.org/wiki/Basel_problem)。 ![\pi^2 = 6 \sum_{k=1}^{\infty} \frac{1}{k^2} = 6 \lim_{k \to \infty} \big( \frac{1}{1^2} + \frac{1}{2^2} + \dots + \frac{1}{k^2} \big) \approx 6 \big( \frac{1}{1^2} + \frac{1}{2^2} + \dots + \frac{1}{n^2} \big)](img/44708a158fc789b494439489536fcd11.jpg) 用于評估截斷總和的簡單 Python 代碼如下所示: ```py # calc_pi.py def recip_square(i): return 1. / i ** 2 def approx_pi(n=10000000): val = 0. for k in range(1, n + 1): val += recip_square(k) return (6 * val) ** .5 ``` 在我的盒子上,這需要大約 4 秒來運行默認 n 的函數。我們選擇 n 越高,![\pi](https://img.kancloud.cn/85/66/8566b582f40fed3aac17eaf207610774_9x7.jpg)的近似值越好。經驗豐富的 Python 程序員已經看到很多地方可以優化這段代碼。但請記住優化的黃金法則:永不優化而不進行分析。讓我重復一遍:**從不**優化而不分析您的代碼。您對代碼的哪一部分花費太多時間的想法是錯誤的。至少,我的總是錯的。所以讓我們編寫一個簡短的腳本來分析我們的代碼: ```py # profile.py import pstats, cProfile import calc_pi cProfile.runctx("calc_pi.approx_pi()", globals(), locals(), "Profile.prof") s = pstats.Stats("Profile.prof") s.strip_dirs().sort_stats("time").print_stats() ``` 在我的盒子上運行它給出以下輸出: ```py Sat Nov 7 17:40:54 2009 Profile.prof 10000004 function calls in 6.211 CPU seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 3.243 3.243 6.211 6.211 calc_pi.py:7(approx_pi) 10000000 2.526 0.000 2.526 0.000 calc_pi.py:4(recip_square) 1 0.442 0.442 0.442 0.442 {range} 1 0.000 0.000 6.211 6.211 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} ``` 它包含代碼在 6.2 CPU 秒內運行的信息。請注意,代碼慢了 2 秒,因為它在 cProfile 模塊中運行。該表包含真正有價值的信息。您可能需要查看 Python [分析文檔](https://docs.python.org/library/profile.html)以獲取詳細信息。這里最重要的列是 totime(在此函數中花費的總時間**而不是**計算由此函數調用的函數)和 cumtime(在此函數中花費的總時間**也計算所謂的函數**通過這個功能)。查看 tottime 列,我們看到大約一半的時間花在 approx_pi 上,另一半花在 recip_square 上。還有半秒鐘在范圍內度過......當然我們應該使用 xrange 進行如此大的迭代。實際上,只需將范圍更改為 xrange 就可以在 5.8 秒內運行代碼。 我們可以在純 Python 版本中進行優化,但由于我們對 Cython 感興趣,讓我們繼續前進并將此模塊帶到 Cython。我們無論如何都會這樣做,以使循環運行得更快。這是我們的第一個 Cython 版本: ```py # cython: profile=True # calc_pi.pyx def recip_square(int i): return 1. / i ** 2 def approx_pi(int n=10000000): cdef double val = 0. cdef int k for k in range(1, n + 1): val += recip_square(k) return (6 * val) ** .5 ``` 注意第一行:我們必須告訴 Cython 應該啟用性能分析。這使得 Cython 代碼稍慢,但如果沒有這個,我們將無法從 cProfile 模塊獲得有意義的輸出。其余代碼大部分都沒有改變,我只輸入了一些可能會加快速度的變量。 我們還需要修改我們的分析腳本以直接導入 Cython 模塊。這是添加 [Pyximport](../userguide/source_files_and_compilation.html#pyximport)模塊導入的完整版本: ```py # profile.py import pstats, cProfile import pyximport pyximport.install() import calc_pi cProfile.runctx("calc_pi.approx_pi()", globals(), locals(), "Profile.prof") s = pstats.Stats("Profile.prof") s.strip_dirs().sort_stats("time").print_stats() ``` 我們只添加了兩行,其余的完全相同。或者,我們也可以手動將代碼編譯成擴展名;我們根本不需要更改配置文件腳本。該腳本現在輸出以下內容: ```py Sat Nov 7 18:02:33 2009 Profile.prof 10000004 function calls in 4.406 CPU seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 3.305 3.305 4.406 4.406 calc_pi.pyx:7(approx_pi) 10000000 1.101 0.000 1.101 0.000 calc_pi.pyx:4(recip_square) 1 0.000 0.000 4.406 4.406 {calc_pi.approx_pi} 1 0.000 0.000 4.406 4.406 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} ``` 我們增加了 1.8 秒。不是太寒酸。將輸出與前一個進行比較,我們看到 recip_square 函數變得更快,而 approx_pi 函數沒有發生很大變化。讓我們更專注于 recip_square 函數。首先請注意,不要從我們模塊之外的代碼調用此函數;所以將它變成 cdef 以減少呼叫開銷是明智的。我們也應該擺脫冪運算符:它變成了 Cython 的 pow(i,2)函數調用,但我們可以改為編寫 i * i,這可能更快。整個功能也是內聯的良好候選者。讓我們看看這些想法的必要變化: ```py # cython: profile=True # calc_pi.pyx cdef inline double recip_square(int i): return 1. / (i * i) def approx_pi(int n=10000000): cdef double val = 0. cdef int k for k in range(1, n + 1): val += recip_square(k) return (6 * val) ** .5 ``` 現在運行配置文件腳本會產生: ```py Sat Nov 7 18:10:11 2009 Profile.prof 10000004 function calls in 2.622 CPU seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 1.782 1.782 2.622 2.622 calc_pi.pyx:7(approx_pi) 10000000 0.840 0.000 0.840 0.000 calc_pi.pyx:4(recip_square) 1 0.000 0.000 2.622 2.622 {calc_pi.approx_pi} 1 0.000 0.000 2.622 2.622 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} ``` 那又給我們買了 1.8 秒。不是我們可以期待的戲劇性變化。為什么 recip_square 仍然在這個表中;應該是內聯的,不是嗎?這樣做的原因是,即使消除了函數調用,Cython 仍會生成分析代碼。讓我們告訴它不再介紹 recip_square;無論如何,我們無法使功能更快: ```py # cython: profile=True # calc_pi.pyx cimport cython @cython.profile(False) cdef inline double recip_square(int i): return 1. / (i * i) def approx_pi(int n=10000000): cdef double val = 0. cdef int k for k in range(1, n + 1): val += recip_square(k) return (6 * val) ** .5 ``` 運行它顯示了一個有趣的結果: ```py Sat Nov 7 18:15:02 2009 Profile.prof 4 function calls in 0.089 CPU seconds Ordered by: internal time ncalls tottime percall cumtime percall filename:lineno(function) 1 0.089 0.089 0.089 0.089 calc_pi.pyx:10(approx_pi) 1 0.000 0.000 0.089 0.089 {calc_pi.approx_pi} 1 0.000 0.000 0.089 0.089 <string>:1(<module>) 1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} ``` 首先要注意的是速度的巨大提升:這個版本只占我們第一個 Cython 版本的 1/50。另請注意,recip_square 已經從我們想要的表中消失了。但最奇特和重要的變化是,about_pi 也變得更快。這是所有分析的問題:在配置文件運行中調用函數會給函數調用增加一定的開銷。這個開銷是**而不是**添加到被調用函數所花費的時間,而是加到**調用**函數所花費的時間。在這個例子中,在最后一次運行中,approx_pi 不需要 2.622 秒;但它調用了 recip_square 10000000 次,每次都需要稍微設置一下它的分析。這相當于大約 2.6 秒的大量時間損失。禁用經常調用的函數的分析現在揭示了 approx_pi 的實際時間;如果需要,我們現在可以繼續優化它。 這個分析教程到此結束。此代碼仍有一些改進空間。我們可以嘗試用 C stdlib 中的 sqrt 調用替換 approx_pi 中的冪運算符;但這并不一定比調用 pow(x,0.5)更快。 即便如此,我們在這里取得的成果非常令人滿意:我們提出了一個比原始 Python 版本快得多的解決方案,同時保留了功能和可讀性。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看