亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

利用 Numba 加速 Python 程序,提速幾十倍

系統(tǒng) 2108 0

作者:chen_h
微信號 & QQ:862251340
微信公眾號:coderpai


當你嫌棄 Python 速度慢時

Python編程語言幾乎可用于任何類型的快速原型設(shè)計和快速開發(fā)。它具有很強的功能,例如它的高級特性,具有幾乎人性化可讀性的語法。此外,它是跨平臺的,具有多樣性的標準庫,它是多范式的,為程序員提供了很多自由,可以使用不同的編程范例,如面向?qū)ο螅δ芑蛘叱绦颉5牵袝r我們系統(tǒng)的某些部分具有高性能要求,因此 Python 提供的速度可能遠遠不夠,那么,我們?nèi)绾卧诓浑x開 Python 領(lǐng)域的情況下提高性能。

其中一個可能的解決方案是使用 Numba,這是一種將 Python 代碼轉(zhuǎn)換為機器指令的運行編輯器,同時讓我們使用 Python 的簡潔和表達能力,并實現(xiàn)機器碼的速度。

什么是 Numba?

Numba 是一個執(zhí)行 JIT 編譯的庫,即使用 LLVM 行業(yè)標準編譯器在運行時將純 Python 代碼轉(zhuǎn)換為優(yōu)化的機器代碼。它還能夠自動并行化在多個核上面運行代碼。Numba 是跨平臺的,因為它適用于不同的操作系統(tǒng)(Linux,Windows,OSX)和不同的架構(gòu)(x86,x86_64,ppc64le等等)。它還能夠在GPU(NVIDIA CUDA 或者 AMD ROC)上運行相同的代碼,并與 Python 2.7 和 3.4-3.7兼容。總的來說,最令人印象深刻的功能是它的使用簡單,因為我們只需要一些裝飾器來充分利用 JIT 的全部功能。

Numba模式和 @jit 裝飾器

最重要的指令是 @jit 裝飾器。正是這個裝飾器指示編譯器運行哪種模式以及使用什么配置。在這種配置下,我們的裝飾函數(shù)的生成字節(jié)碼與我們在裝飾器中指定的參數(shù)(例如輸入?yún)?shù)的類型)相結(jié)合,進行分析,優(yōu)化,最后使用 LLVM 進行編譯,生成特定定制的本機機器指令。然后,為每個函數(shù)調(diào)用此編譯版本。

有兩種重要的模式:nopython和object。noPython 完全避免了 Python 解釋器,并將完整代碼轉(zhuǎn)換為可以在沒有 Python 幫助的情況下運行的本機指令。但是,如果由于某種原因,該模式不可用(例如,當使用不受支持的 Python功能或者外部庫時),編譯將回退到對象模式,當它無法編譯某些代碼時,它將使用 Python 解釋器。當然,nopython 模式是提供最佳性能提升的模式。

Numba 的高層架構(gòu)

Numba 的轉(zhuǎn)換過程可以在一系列重要步驟中進行轉(zhuǎn)換,從字節(jié)碼分析到最終機器代碼生成。下面的圖片說明了這個過程,其中綠色框?qū)?yīng)于 Numba 編譯器的前端,藍色框?qū)儆诤蠖恕?

利用 Numba 加速 Python 程序,提速幾十倍_第1張圖片

Numba 編譯器首先對所需函數(shù)的 byecode 進行分析。此步驟生成一個描述可能的執(zhí)行流程的圖表,稱為控制流程圖(CFG)。基于該圖,我們就可以計算分析整個過程。完成這些步驟后,編譯器開始將字節(jié)碼轉(zhuǎn)換為中間表示(IR),Numba 將執(zhí)行進一步的優(yōu)化和轉(zhuǎn)換。然后,執(zhí)行類型推斷,這是最重要的步驟之一。在此步驟中,編譯器將嘗試推斷所有變量的類型。此外,如果啟用并行設(shè)置,IR 代碼將轉(zhuǎn)換為等效的并行版本。

如果成功推斷出所有類型,則將 Numba IR代碼轉(zhuǎn)換為有效的 LLVM IR 代碼。但是,如果類型推斷過程失敗,LLVM 生成的代碼將會變慢,因為它仍然需要處理對 Python C API 的調(diào)用。最后,LLVM IR 代碼由 LLVM JIT 編譯器編譯為本機指令。然后將這個優(yōu)化的加工代碼加載到內(nèi)存中,并在對同一函數(shù)的多次調(diào)用中重用,使其比純 Python 快數(shù)百倍。

出于調(diào)試目的,Numba 還提供了一組可以啟用的標志,以便查看不同階段的輸出。

            
              os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_CFG"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_IR"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_ANNOTATION"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DEBUG_ARRAY_OPT_STATS"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_LLVM"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_OPTIMIZED"
              
              
                ]
              
              
                =
              
              
                "1"
              
              
os
              
                .
              
              environ
              
                [
              
              
                "NUMBA_DUMP_ASSEMBLY"
              
              
                ]
              
              
                =
              
              
                "1"
              
            
          

加速運算的一個例子

我們可以使用 Numba 庫的一個絕佳例子是進行密集的數(shù)值運算。舉個例子,讓我們計算一組 2 16 2^{16} 2 1 6 個隨機數(shù)的 softmax 函數(shù)。softmax 函數(shù),用于將一組實際值轉(zhuǎn)換為概率并通常用作神經(jīng)網(wǎng)絡(luò)體系結(jié)構(gòu)中的最后一層,定義為:

利用 Numba 加速 Python 程序,提速幾十倍_第2張圖片

下面的代碼顯示了這個函數(shù)的兩個不同的實現(xiàn),一個純 Python 方法,一個使用 numba 和 numpy 的優(yōu)化版本:

            
              
                import
              
               time

              
                import
              
               math

              
                import
              
               numpy 
              
                as
              
               np

              
                from
              
               numba 
              
                import
              
               jit


@jit
              
                (
              
              
                "f8(f8[:])"
              
              
                ,
              
               cache
              
                =
              
              
                False
              
              
                ,
              
               nopython
              
                =
              
              
                True
              
              
                ,
              
               nogil
              
                =
              
              
                True
              
              
                ,
              
               parallel
              
                =
              
              
                True
              
              
                )
              
              
                def
              
              
                esum
              
              
                (
              
              z
              
                )
              
              
                :
              
              
                return
              
               np
              
                .
              
              
                sum
              
              
                (
              
              np
              
                .
              
              exp
              
                (
              
              z
              
                )
              
              
                )
              
              


@jit
              
                (
              
              
                "f8[:](f8[:])"
              
              
                ,
              
               cache
              
                =
              
              
                False
              
              
                ,
              
               nopython
              
                =
              
              
                True
              
              
                ,
              
               nogil
              
                =
              
              
                True
              
              
                ,
              
               parallel
              
                =
              
              
                True
              
              
                )
              
              
                def
              
              
                softmax_optimized
              
              
                (
              
              z
              
                )
              
              
                :
              
              
    num 
              
                =
              
               np
              
                .
              
              exp
              
                (
              
              z
              
                )
              
              
    s 
              
                =
              
               num 
              
                /
              
               esum
              
                (
              
              z
              
                )
              
              
                return
              
               s



              
                def
              
              
                softmax_python
              
              
                (
              
              z
              
                )
              
              
                :
              
              
    s 
              
                =
              
              
                [
              
              
                ]
              
              

    exp_sum 
              
                =
              
              
                0
              
              
                for
              
               i 
              
                in
              
              
                range
              
              
                (
              
              
                len
              
              
                (
              
              z
              
                )
              
              
                )
              
              
                :
              
              
        exp_sum 
              
                +=
              
               math
              
                .
              
              exp
              
                (
              
              z
              
                [
              
              i
              
                ]
              
              
                )
              
              
                for
              
               i 
              
                in
              
              
                range
              
              
                (
              
              
                len
              
              
                (
              
              z
              
                )
              
              
                )
              
              
                :
              
              
        s 
              
                +=
              
              
                [
              
              math
              
                .
              
              exp
              
                (
              
              z
              
                [
              
              i
              
                ]
              
              
                )
              
              
                /
              
               exp_sum
              
                ]
              
              
                return
              
               s



              
                def
              
              
                main
              
              
                (
              
              
                )
              
              
                :
              
              
    np
              
                .
              
              random
              
                .
              
              seed
              
                (
              
              
                0
              
              
                )
              
              
    z 
              
                =
              
               np
              
                .
              
              random
              
                .
              
              uniform
              
                (
              
              
                0
              
              
                ,
              
              
                10
              
              
                ,
              
              
                10
              
              
                **
              
              
                8
              
              
                )
              
              
                # generate random floats in the range [0,10)
              
              

    start 
              
                =
              
               time
              
                .
              
              time
              
                (
              
              
                )
              
              
    softmax_python
              
                (
              
              z
              
                .
              
              tolist
              
                (
              
              
                )
              
              
                )
              
              
                # run pure python version of softmax
              
              
    elapsed 
              
                =
              
               time
              
                .
              
              time
              
                (
              
              
                )
              
              
                -
              
               start
    
              
                print
              
              
                (
              
              
                'Ran pure python softmax calculations in {} seconds'
              
              
                .
              
              
                format
              
              
                (
              
              elapsed
              
                )
              
              
                )
              
              

    softmax_optimized
              
                (
              
              z
              
                )
              
              
                # cache jit compilation
              
              
    start 
              
                =
              
               time
              
                .
              
              time
              
                (
              
              
                )
              
              
    softmax_optimized
              
                (
              
              z
              
                )
              
              
                # run optimzed version of softmax
              
              
    elapsed 
              
                =
              
               time
              
                .
              
              time
              
                (
              
              
                )
              
              
                -
              
               start
    
              
                print
              
              
                (
              
              
                '\nRan optimized softmax calculations in {} seconds'
              
              
                .
              
              
                format
              
              
                (
              
              elapsed
              
                )
              
              
                )
              
              
                if
              
               __name__ 
              
                ==
              
              
                '__main__'
              
              
                :
              
              
    main
              
                (
              
              
                )
              
            
          

上述腳本的輸出結(jié)果為:

            
              Ran pure python softmax calculations 
              
                in
              
              
                77.56219696998596
              
               seconds

Ran optimized softmax calculations 
              
                in
              
              
                1.517017126083374
              
               seconds

            
          

這些結(jié)果清楚的顯示了將代碼轉(zhuǎn)換為 Numba 能夠理解的代碼時獲得的性能提升。

在 softmax_optimized 函數(shù)中,已經(jīng)存在 Numba 注釋,它充分利用了 JIT 優(yōu)化的全部功能。事實上,在編譯過程中,以下字節(jié)碼將被分析,優(yōu)化并編譯為本機指令:

            
              
                >
              
               python

              
                import
              
               dis

              
                from
              
               softmax 
              
                import
              
               esum
              
                ,
              
               softmax_optimized

              
                >>
              
              
                >
              
               dis
              
                .
              
              dis
              
                (
              
              softmax_optimized
              
                )
              
              
                14
              
              
                0
              
               LOAD_GLOBAL              
              
                0
              
              
                (
              
              np
              
                )
              
              
                2
              
               LOAD_ATTR                
              
                1
              
              
                (
              
              exp
              
                )
              
              
                4
              
               LOAD_FAST                
              
                0
              
              
                (
              
              z
              
                )
              
              
                6
              
               CALL_FUNCTION            
              
                1
              
              
                8
              
               STORE_FAST               
              
                1
              
              
                (
              
              num
              
                )
              
              
                15
              
              
                10
              
               LOAD_FAST                
              
                1
              
              
                (
              
              num
              
                )
              
              
                12
              
               LOAD_GLOBAL              
              
                2
              
              
                (
              
              esum
              
                )
              
              
                14
              
               LOAD_FAST                
              
                0
              
              
                (
              
              z
              
                )
              
              
                16
              
               CALL_FUNCTION            
              
                1
              
              
                18
              
               BINARY_TRUE_DIVIDE
             
              
                20
              
               STORE_FAST               
              
                2
              
              
                (
              
              s
              
                )
              
              
                16
              
              
                22
              
               LOAD_FAST                
              
                2
              
              
                (
              
              s
              
                )
              
              
                24
              
               RETURN_VALUE

              
                >>
              
              
                >
              
               dis
              
                .
              
              dis
              
                (
              
              esum
              
                )
              
              
                9
              
              
                0
              
               LOAD_GLOBAL              
              
                0
              
              
                (
              
              np
              
                )
              
              
                2
              
               LOAD_ATTR                
              
                1
              
              
                (
              
              
                sum
              
              
                )
              
              
                4
              
               LOAD_GLOBAL              
              
                0
              
              
                (
              
              np
              
                )
              
              
                6
              
               LOAD_ATTR                
              
                2
              
              
                (
              
              exp
              
                )
              
              
                8
              
               LOAD_FAST                
              
                0
              
              
                (
              
              z
              
                )
              
              
                10
              
               CALL_FUNCTION            
              
                1
              
              
                12
              
               CALL_FUNCTION            
              
                1
              
              
                14
              
               RETURN_VALUE

            
          

我們可以通過簽名提供有關(guān)預期輸入和輸出類型的更多信息。在上面的示例中,簽名"f8[:](f8[:])" 用于指定函數(shù)接受雙精度浮點數(shù)組并返回另一個 64 位浮點數(shù)組。

簽名也可以使用顯式類型名稱:“float64 [:](float64 [:])”。一般形式是類型(類型,類型,…),類似于經(jīng)典函數(shù),其中參數(shù)名稱由其類型替換,函數(shù)名稱由其返回類型替換。Numba 接受許多不同的類型,如下所述:

Type name(s) Type short name Description
boolean b1 represented as a byte
uint8, byte u1 8-bit unsigned byte
uint16 u2 16-bit unsigned integer
uint32 u4 32-bit unsigned integer
uint64 u8 64-bit unsigned integer
int8, char i1 8-bit signed byte
int16 i2 16-bit signed integer
int32 i4 32-bit signed integer
int64 i8 64-bit signed integer
intc C
uintc C
intp pointer-sized integer
uintp pointer-sized unsigned integer
float32 f4 single-precision floating-point number
float64, double f8 double-precision floating-point number
complex64 c8 single-precision complex number
complex128 c16 double-precision complex number

這些注釋很容易使用 [:],[: , :] 或者 [: , : , ;] 分別擴展為數(shù)組形式,分別用于 1,2和3維。

最后

Python 是一個非常棒的工具。但是,它也有一些限制,但是我們可以通過一些別的途徑來提高它的性能,本文介紹的 Nubma 就是一種非常好的方式。


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 成人二区| 一级高清毛片 | 国产成社区在线视频观看 | 久久这里有精品视频任我鲁 | 色噜噜视频| 国产国语一级毛片中文 | 国产精品久久亚洲一区二区 | 久久红综合久久亚洲网色 | 日韩成人影视 | 亚洲欧洲日本精品 | 国产视频在线播放 | 日韩精品欧美成人 | 99在线观看免费视频 | 久久精品一区二区三区不卡牛牛 | 日本亚洲精品成人 | 日韩一级片在线观看 | 国产伊人精品 | 国产免费区 | 日本中文字幕一区二区有码在线 | 日韩国产精品99久久久久久 | 中文字幕伦理聚合第一页 | 久久精品道一区二区三区 | 成人性生免费视频 | 久久99精品久久久久久 | 久久精品国产欧美日韩99热 | 欧美α片无限看在线观看免费 | 91精品国产免费久久久久久青草 | 亚洲视频在线一区 | 三级aa久久 | 免费观看成人www精品视频在线 | 亚洲一区二区三区麻豆 | 一集毛片 | 色综合色狠狠天天综合色hd | 久久婷婷五月综合色丁香 | 亚洲欧美国产高清va在线播放 | se在线观看| 男人边吃奶边爱边做视频日韩 | h片免费在线观看 | 亚洲一区二区欧美日韩 | 久久久美女视频 | 老司机永久免费网站在线观看 |