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

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

系統(tǒng) 1866 0

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


當(dāng)你嫌棄 Python 速度慢時(shí)

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

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

什么是 Numba?

Numba 是一個(gè)執(zhí)行 JIT 編譯的庫,即使用 LLVM 行業(yè)標(biāo)準(zhǔn)編譯器在運(yùn)行時(shí)將純 Python 代碼轉(zhuǎn)換為優(yōu)化的機(jī)器代碼。它還能夠自動(dòng)并行化在多個(gè)核上面運(yùn)行代碼。Numba 是跨平臺的,因?yàn)樗m用于不同的操作系統(tǒng)(Linux,Windows,OSX)和不同的架構(gòu)(x86,x86_64,ppc64le等等)。它還能夠在GPU(NVIDIA CUDA 或者 AMD ROC)上運(yùn)行相同的代碼,并與 Python 2.7 和 3.4-3.7兼容??偟膩碚f,最令人印象深刻的功能是它的使用簡單,因?yàn)槲覀冎恍枰恍┭b飾器來充分利用 JIT 的全部功能。

Numba模式和 @jit 裝飾器

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

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

Numba 的高層架構(gòu)

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

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

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

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

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

            
              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"
              
            
          

加速運(yùn)算的一個(gè)例子

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

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

下面的代碼顯示了這個(gè)函數(shù)的兩個(gè)不同的實(shí)現(xiàn),一個(gè)純 Python 方法,一個(gè)使用 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 能夠理解的代碼時(shí)獲得的性能提升。

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

            
              
                >
              
               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)預(yù)期輸入和輸出類型的更多信息。在上面的示例中,簽名"f8[:](f8[:])" 用于指定函數(shù)接受雙精度浮點(diǎn)數(shù)組并返回另一個(gè) 64 位浮點(diǎn)數(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

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

最后

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


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

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

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

【本文對您有幫助就好】

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

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 中文字幕在线高清 | 日本特级黄毛片毛片视频 | 四虎视频在线精品免费观看 | 爱操视频在线观看 | 欧美精品v欧洲高清 | 久久93精品国产91久久综合 | 成人a大片高清在线观看 | 一区二区日韩欧美 | 日本在线观看成人小视频 | 久久精品在线播放 | 在线成人播放毛片 | 中文国产成人精品久久96 | 亚洲国产成人久久综合碰 | 中文字幕在线欧美 | 成人国产亚洲欧美成人综合网 | 福利在线免费 | 一级香蕉视频在线观看 | 国产精品毛片一区二区三区 | 亚洲成a v人片在线观看 | 天天干天天干天天干 | 成人精品第一区二区三区 | 免费看黄色片视频 | 中文一区二区 | 亚洲va欧美va国产va天堂影 | 日本不卡影院 | 欧美人牲囗毛片 | 热久久久久久 | 免费人成年短视频在线观看网站 | 女人一级毛片免费观看 | 久久久久亚洲精品一区二区三区 | 亚洲美女在线观看播放 | 国内精品久久久久久久久久影视 | 色www精品视频在线观看 | 狠狠色婷婷综合天天久久丁香 | 日韩欧美伦理 | 国产亚洲综合成人91精品 | 福利毛片 | 四虎国产永久免费久久 | 无遮挡一级毛片视频 | 91福利一区二区在线观看 | 五月婷婷之综合激情 |