小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

阿里巴巴楊群:高并發(fā)場景下Python的性能挑戰(zhàn)

 車?yán)遄覸 2019-08-06

4月13日,在CSDN主辦的“2019 Python開發(fā)者日”大會上,阿里云數(shù)據(jù)庫專家楊群分享了《高并發(fā)場景下Python的性能挑戰(zhàn)》的主題演講。

以下為演講整理,文章略有刪減:

性能問題

▌(一)GIL

為什么大家都說Python慢?最主要的原因是全局解釋器鎖。今天講的Python是官方的C版Python。CPython在創(chuàng)建變量時,首先對變量分配內(nèi)存,然后開始計數(shù)變量的數(shù)量,大家提出稱之為“引用計數(shù)”。在引用計數(shù)變?yōu)?時,從系統(tǒng)中釋放變量的內(nèi)存。如果多個線程同時對這個計數(shù)做操作,線程不安全,會導(dǎo)致很多問題。

綜合垃圾回收機(jī)制問題,CPython引入了GIL,同一個時刻在一個進(jìn)程允許一個線程使用解釋器,意味著單進(jìn)程下Python多線程的性能沒有那么好。這樣做的好處在于能夠避免死鎖和數(shù)據(jù)用戶安全方面的問題。

Python有三種線程狀態(tài):Idle、Running、Failed GIL Acquire。曾經(jīng)有人對GIL的性能影響做了兩個測試。第一個測試案例是兩個CPU密集線程,代碼運(yùn)行過程的大部分狀態(tài)是Failed GIL Acquire,兩個線程的運(yùn)行沒有達(dá)到雙核的效果。

第二個案例是IO密集型的線程。仔細(xì)分析發(fā)現(xiàn),IO沒有達(dá)到想象的預(yù)期效果。所以IO密集型和CPU密集型同時存在時,IO密集型未必達(dá)到想要的運(yùn)算速度,我們要區(qū)分好IO密集型和CPU密集型的服務(wù)。

▌(二)解釋器

CPython要首先生成pcy字節(jié)碼序列,之后才能被CPU理解,所以較慢。JAVA、.NET也有中間的翻譯,但因為JAVA和.NET使用即時編輯(JIT),使用JIT可以檢測哪些代碼執(zhí)行得比較多,意味著計算機(jī)應(yīng)用程序需要重復(fù)做一件事情的時候它就會更快。

▌(三)動態(tài)語言

Python是動態(tài)語言類型,我們在做類型轉(zhuǎn)化或者比較的時候比較耗時,因為讀取、寫入變量或者引用變量時會進(jìn)行檢查。靜態(tài)類型語言沒有這么高的靈活性,但它已經(jīng)規(guī)定好了內(nèi)存中的狀態(tài),所以很快。

Python這么慢,我們?yōu)槭裁催€要用它?一是用Python優(yōu)雅、簡潔。二是大多數(shù)應(yīng)用場景時,GIL或者解釋器帶來的性能未必是我們所擔(dān)心的,比如科學(xué)計算或者平常做一些數(shù)據(jù)分析或小應(yīng)用時不會考慮到這個問題。

服務(wù)選型

640?wx_fmt=png

             這是市面上常用的web框架針對Python的領(lǐng)域做服務(wù)選型分析的框架。無論使用什么web框架,在web服務(wù)中都會選擇多進(jìn)程。一方面考慮到服務(wù)需要一定的可用性,需要多進(jìn)程來保證減少服務(wù)可用性的影響。另外,多個進(jìn)程意味著多個解釋器,多個解釋器意味著我們盡量減少GIL帶來的性能影響。

640?wx_fmt=png

這是常見web服務(wù)的方法,前端的LoadBalancer,大家可能會選擇常見的Nginx、apache或者云服務(wù)的SLB。

異步IO框架的選擇是大家都關(guān)心的一個問題。GIL如果是IO密集型,我們用異步能夠做到很快。但是它有很適合的應(yīng)用場景,比如不想做Nginxluv插件,作為高性能的擴(kuò)展方案,那就用tornado來寫,如果內(nèi)部代碼全是異步的IO操作,它是非常好的,可以組裝自己的邏輯,比如積數(shù)之類的都可以放在tornado里來做,性能可以得到保障。

另外,PyPy是Python的Just in time 編譯器,性能一般要比CPython解釋器至少好3倍。但是它和JIT編譯器一樣有啟動慢的特點,所以適合對重啟不是很敏感的服務(wù)。它的問題是不支持C擴(kuò)展的Python庫。

性能瓶頸分析

在現(xiàn)實業(yè)務(wù)開發(fā)中,最主要的是依靠業(yè)務(wù)日志分析,考慮我們的業(yè)務(wù)鏈路中是否存在網(wǎng)絡(luò)耗時。對一些任務(wù)日志可以用AWK或者unit等,去分析出來哪些接口訪問量比較多、耗時嚴(yán)重的,使用Cprofile等工具分析問題存在哪里,然后再找到合適的優(yōu)化方向。

640?wx_fmt=png

這是一個簡單的Cprofile例子,執(zhí)行def1、def2、def3,去分析一下它的耗時情況。

640?wx_fmt=png

上面的代碼中有多個函數(shù)的執(zhí)行??梢钥吹剑詈笠淮蔚倪\(yùn)行耗時是237毫秒。當(dāng)然,對于profile也可以輸出pstat格式的數(shù)據(jù),大家能通過可視化清楚的看到自己函數(shù)耗時占比。

優(yōu)化方法

▌(一)原則

第一,優(yōu)化時一定要靠數(shù)據(jù)說話。即使需要犧牲一次迭代去更新一下,也要把數(shù)據(jù)羅列出來,使之有理有據(jù)。我們優(yōu)化的原則主要有四點:一是用數(shù)據(jù)說話,數(shù)據(jù)不只是優(yōu)化的原因,也是優(yōu)化的方向,把指標(biāo)達(dá)到一定水準(zhǔn),目的才達(dá)到了;第二,不要過早優(yōu)化或過度優(yōu)化。否則有可能出現(xiàn)業(yè)務(wù)偏差;第三,深入理解業(yè)務(wù)。對產(chǎn)品更加負(fù)責(zé);第四,選擇好的衡量標(biāo)準(zhǔn),比如CPU利用率降到多少了。

▌(二)IO密集型

如果是IO密集型的服務(wù),使用多線程實際比單線程的性能提高很多。但是如果大量IO操作都比較耗時,它的性能未必像想象中那么好。這種情況下建議批量操作,或者改為協(xié)程,網(wǎng)絡(luò)帶寬性能會帶來很大的提升。此外,減少IO操作也是可行方案。

▌(三)CPU密集型

多線程顯然已經(jīng)不適用于CPU密集型的服務(wù),因為頻繁的GIL爭搶會導(dǎo)致序性能大幅度下降。多進(jìn)程其實很適合CPU密集型服務(wù)。對于CPU密集型的服務(wù),為了減少解釋器的損耗 ,最好可以適用C的擴(kuò)展庫來提高程序性能,能夠一定程度緩解類型轉(zhuǎn)換帶來的性能損耗 ,而且可以大幅度提高基礎(chǔ)庫的運(yùn)行速度。

▌(四)緩存

緩存一直是系統(tǒng)性能優(yōu)化的利器,這對Python是架構(gòu)性的東西,可能跟語言的相關(guān)性沒有那么大。但是Python的編程方法對緩存代碼改造是非常便利的。

640?wx_fmt=png

這是緩存的例子,這個業(yè)務(wù)邏輯很簡單,在現(xiàn)有的生產(chǎn)模型里比較常用。

640?wx_fmt=png

這是一個有緩存的函數(shù),我們在性能調(diào)優(yōu)時需要動態(tài)去允許開關(guān)函數(shù)不緩存,必須按照原來的方式執(zhí)行一遍才能拿到結(jié)果。這里有一個計算緩存過程,mode是我們開發(fā)的模式,可以在函數(shù)動態(tài)的取mode,達(dá)到開關(guān)的值。我們可以通過這個開關(guān)去讓函數(shù)得到它執(zhí)行的方式。

另外,我們在存儲序列化數(shù)據(jù)時最好使用高性能的庫,比如cPickle,cPickle雖然比pickle,但是沒有cJSON快。可以給存儲層、DB層、計算的函數(shù)層、應(yīng)用層都加上緩存,但是在Python應(yīng)用程序之外也有很多架設(shè)高速緩存的方法。

多層緩存雖然是一個架構(gòu)緩存,但是Python開發(fā)做擴(kuò)展性應(yīng)用時,用戶體驗是非常好的,簡短的代碼開發(fā)就可以完成通用功能,而且里面的語言不用動。

▌(五)懶加載

640?wx_fmt=png

       還有一些常用的方法,比如懶加載。這是常用的Lazy單例,調(diào)用一次之后就不再調(diào)用了,以后拿到的是初始化好的。

▌(六)一些技巧

640?wx_fmt=png

對于generator需要謹(jǐn)慎對待。 對于循環(huán)遍歷,比如遍歷10萬個數(shù)據(jù),generator有可能更慢一些,這種東西是需要分場合的。如果在循環(huán)中不需要把所有列表生成出來,那么速度會稍微快一些。

640?wx_fmt=png

這是一個命名空間問題。第一種狀況可能更簡單一些,但是它是147毫秒,第二種狀況是把循環(huán)函數(shù)里,快了1倍時間。這是因為Python在執(zhí)行代碼時遇到了range。對于第一種,Python首先會在本地的變量里找這個range,如果沒有找到會去gloabl變量里找range。

對于第二種,range的查找不需要再走gloabl,它走的是load-const,這是一個很快的過程。有些由于空間導(dǎo)致的性能微小的差距,執(zhí)行少量數(shù)據(jù)時看不出來,但是大量數(shù)據(jù)時是非常明顯的。

總結(jié)

Python這種便利的特性給我們帶來很大的開發(fā)優(yōu)勢:

數(shù)據(jù)分析是第一位的,要去優(yōu)化自己的Python服務(wù)。

第二,需要合理的測試環(huán)境,不要因為性能調(diào)優(yōu)而影響增加的服務(wù)穩(wěn)定性或者出現(xiàn)故障。

第三,要有的放矢,我們有時面對更多服務(wù)拆分或微服務(wù)化,對架構(gòu)說不定有更多好處。比如把IO密集型服務(wù)和CPU密集型服務(wù)分開做,在前端使用IO密集型的操作。將所有的請求都集中在對外的入口,這樣對外服務(wù)的性能會得到很大的提高,因為性能壓力都分散到各個微服務(wù)里了,而同樣的性能得到了最大的保障。大家可以多鉆研一下,掌握一些技巧。

謝謝大家。

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多