python性能解決_我們如何發現并解決Python代碼中性能下降的問題

Python部落(python.freelycode.com)組織翻譯,禁止轉載,歡迎轉發。

作者:Omer Lachish

最近,我們已經開始使用RQ庫代替Celery庫作為我們的任務運行引擎。第一階段,我們只遷移了那些不直接進行查詢工作的任務。這些任務包括發送電子郵件,確定哪些查詢需要刷新、記錄用戶事件和其他維護工作。

在部署完成之后我們注意到,任務數量相同的情況下,與Celery庫相比,RQ庫需要更多的CPU去執行這些任務。我想我應該分享一下我是如何分析和解決這個問題的。

關于Celery庫和RQ庫的區別

Celery庫和RQ庫都有工作進程的概念,而且都使用分支來允許多種任務并行運行。當你啟動一個Celery工作進程時,它會分到幾個不同的進程,每個進程會自主地處理任務。使用RQ庫,一個主進程將只會實例化一個子進程(稱為“Worker Horse”),該子進程在執行完一個單獨的任務后將會結束。當主進程從隊列中提取另一項任務時,它將會派出一個新的Work Horse。

在RQ庫中,通過使用更多的工作進程,即可實現與Celery相同的并行性。 但是,Celery庫和RQ庫之間存在著細微的區別:Celery進程在啟動時會實例化多個子進程,并將其重用于多個任務。 而使用RQ庫時,你必須給每一個任務分配進程。 兩種方法都有優點和缺點,但這些內容不在本文討論范圍之內。

標桿分析法

在介紹任何內容之前,我想要確定一個基準,即一個工作容器處理1000項任務需要多長時間。我決定把重心放在record_event工作上,因為它是一種頻繁的,輕量級的操作。我使用time命令來衡量性能,這里需要修改一下源代碼:為了得出完成1000項任務所需的時間,我傾向于采用RQ庫的burst模式,該模式在處理完作業后會退出流程。

我想避免測量那些被安排在基準測試時間段的任務。因此,在task/general.py中的record_event聲明上方,通過將@job('default')替換為@job('benchmark'),可以將record_event移至一個名為benchmark的專用隊列。

現在我們可以開始計時了。首先,我想查看一個進程啟動和停止需要多長時間(沒有任何工作)以便之后可以從任何結果中減去該時間:

2bde55c395f544888557636131fc295a.png

在我的計算機上,進程初始化需要14.7秒。我會記住這個時間。

然后,我將1000個虛擬的record_event任務添加進benchmark隊列中:

39fe1e45871f473790dce2471070bc89.png

現在,運行相同的命令,看看處理1000項任務需要多長時間:

6a5929bec88e4f31905b93e9377863ac.png

減去14.7秒的啟動時間,我們看到4個進程處理1000項任務需要102秒。現在,讓我們嘗試找出原因!為此,在進程工作的同時,我們將使用py_spy模塊。

分析

讓我們再增加1000項任務(因為上次的測試已經刪除了所有任務),運行進程并同時監控它們所耗費的時間:

3122074069c94f55b69f820a849249bd.png

我知道,最后一條命令非常短。 理想情況下,出于可讀性考慮,我會在每個“ &&”上都打斷該命令,但是這些命令應該在同一個docker-compose exec worker bash session中按順序運行。所以,以下是其功能的快速分析:在后臺中,burst模式下,開啟了4個進程。

等待15秒(大致讓它們完成啟動。

安裝py-spy模塊。

運行rq-info命令,并且為其中一個進程進行分層控制。

在該控制過程中記錄10秒中的活動情況并將其保存到profile.svg文件中。

結果如下火焰圖所示:

557480556f3c4c37b6c5333a55e08ab3.png

從火焰圖中,我注意到record_event在sqlalchemy.orm.configure_mappers中花費了很大一部分的執行時間,并且每次處理一項工作時這種情況都會出現。從它們的文檔當中,我知道了:初始化到目前為止已構建的所有映射器的映射器間關系。

的確不需要在每一個分支上都發生這種事情。所以,我們可以在主進程當中一次性地初始化這些關系,避免在多個子進程當中重復性這些工作。

因此,在啟動進程之前,我已經對sqlalchemy.org.configure_mappers()進行了調用,并再次進行了測試:

f63a095797c04f9d90a23a63cb0f89cd.png

如果我們減去14.7秒的啟動時間,4個線程處理1000項任務的時間將從102秒減少到24.6秒。 比以前提高了4倍! 通過修復此程序,我們成功地將RQ生產資源減少了4倍,并保持了相同的吞吐量。

我認為,你應該記住,在單線程和多線程的情況下,應用的行為是有所不同的。如果每一項任務沒有繁重的重復的工作要做,通常最好在分到多個線程之前進行一次。這些事情在測試和開發過程中不會出現,因此請確保您進行充分的測試并挖掘出任何會出現的性能問題。英文原文:https://blog.redash.io/how-we-spotted-and-fixed-a-performance-degradation-in-our-python-code/

譯者:Lyx

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/374324.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/374324.shtml
英文地址,請注明出處:http://en.pswp.cn/news/374324.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

easyui $.parser.parse 頁面重新渲染

一些dom元素是動態拼接上的easui的樣式,由于頁面已經渲染過了,所以需要手動執行渲染某個部件或者整個頁面 $.parser.parse(); // parse all the page $.parser.parse(#cc); // parse the specified node $.parser.parse($("#grid").parent());…

Java EE6裝飾器:在注入時裝飾類

軟件中常見的設計模式是裝飾器模式 。 我們上一堂課,然后在它周圍包裝另一堂課。 這樣,當我們調用類時,我們總是在到達內部類之前經過周圍的類。 Java EE 6允許我們通過CDI創建裝飾器,作為其AOP功能的一部分。 如果我們想實現仍然…

C語言代碼規范(六)浮點型變量邏輯比較

無論是float還是double類型的變量&#xff0c;都有精度限制。所以一定要避免將浮點變量用""或"!"與數字比較&#xff0c;應該設法轉化成為">"或"<"形式。 不建議使用的例子&#xff1a; if(0.0 x) if(0.0 ! x) 強烈推薦的例…

圖靈機器人調用數據恢復_機器人也能撩妹?python程序員自制微信機器人,替他俘獲女神芳心...

機器人也有感情還記得王傳君飾演的《星語心愿之再愛》這部電影嗎&#xff1f;王傳君飾演的天才程序員“王鵬鵬”因工作原因不能陪伴照顧身在異地的女朋友“林亦男”&#xff0c;呆萌宅男“王鵬鵬”開發出一款以自己為原型的“王鵬鵬8.0”程序去陪伴異地戀的女友&#xff0c;后來…

Spark排錯與優化

一. 運維 1. Master掛掉,standby重啟也失效 Master默認使用512M內存&#xff0c;當集群中運行的任務特別多時&#xff0c;就會掛掉&#xff0c;原因是master會讀取每個task的event log日志去生成spark ui&#xff0c;內存不足自然會OOM&#xff0c;可以在master的運行日志中看到…

在MySQL上使用帶密碼的GlassFish JDBC安全性

我在該博客上最成功的文章之一是有關在GlassFish上使用基于表單的身份驗證來建立JDBC安全領域的文章 。 對這篇文章的一些評論使我意識到&#xff0c;要真正使它安全&#xff0c;應該做的還很多。 開箱即用的安全性 圖片&#xff1a; TheKenChan &#xff08; CC BY-NC 2.0 &a…

mgo寫入安全機制

mgo寫入安全機制 mongo寫入安全mgo寫入安全mongo寫入安全 mongo本身也有一整套的寫入安全機制,但是在這篇的內容里只介紹一小部分相關部分.先放一個鏈接可以跳過本節不看直接看這個 鏈接. WriteConcern.NONE:沒有異常拋出WriteConcern.NORMAL:僅拋出網絡錯誤異常&#xff0c;沒…

C學習雜記(二)筆試題:不使用任何中間變量如何將a、b的值進行交換

常見的方法如下 void swap1(int *a, int *b) {int temp *a;*a *b;*b temp; } 不使用中間變量的方法 void swap2(int *a, int *b) {*a *a *b;*b *a - *b;*a *a - *b; } 這種方法是不可取的&#xff0c;因為ab和a-b的運算可能會導致數據溢出。 void swap3(int *a, in…

利用python進行數據分析_利用python進行數據分析復現(1)

&#xfeff;一直以來&#xff0c;都想學習python數據分析相關的知識&#xff0c;總是拖拖拉拉&#xff0c;包括這次這個分享也是。《利用python進行數據分析 第2版》是一次無意之間在簡書上看到的一個分享&#xff0c;我決定將很詳細。一直都想著可以復現一下。但總有理由&…

在運行時交換出Spring Bean配置

如今&#xff0c;大多數Java開發人員都定期與Spring打交道&#xff0c;而我們當中的許多人已經熟悉了Spring的功能和局限性。 最近&#xff0c;我遇到了一個我從未遇到過的問題&#xff1a;引入了基于運行時引入的配置來重新連接Bean內部的功能。 這對于簡單的配置更改或交換掉…

Proximal Algorithms--Accelerated proximal gradient method

4.3 Accelerated proximal gradient method&#xff1a; 加速近端梯度方法&#xff1a; 基本的近端梯度方法的所謂的“加速”版本&#xff0c;就是在算法中包含了一個外推(extrapolation)步驟&#xff0c;一個簡單的版本是&#xff1a; yk1:xkωk(xk?xk?1)xk1:proxλkg(yk1?…

C語言代碼規范(七)#define

#define 宏定義的使用 #define MAX(x, y) ( ((x) > (y)) ? (x) : (y) ) #define MIN(x, y) ( ((x) < (y)) ? (x) : (y) ) 在宏定義中要把參數用括號擴起來( ((x) > (y)) ? (x) : (y) )。 因為宏只是簡單的文本替換&#xff0c;如果不注意&#xff0c;很容…

http 二進制_淺談HTTP協議

HTTP一、HTTP協議http協議&#xff0c;是超文本傳輸協議&#xff0c;此協議是基于TCP/IP的協議&#xff0c;是互聯網上應用最為廣泛的一直網絡協議是一種無狀態協議&#xff0c;默認端口為80,。設計HTTP的最初目的是為了提供一種發布和接受HTML頁面的方法。通過HTTP或者HTTPS協…

登陸注冊

登陸注冊&#xff0c;注冊的賬號存在服務器的數據庫里&#xff0c;成功了就給你返回成功&#xff0c;失敗了就返回失敗 有三種登陸方式&#xff1a;普通注冊&#xff0c;手機號注冊&#xff0c;第三方注冊轉載于:https://www.cnblogs.com/SensenCoder/p/4885606.html

Java并發教程–線程池

Java 1.5中提供的最通用的并發增強功能之一是引入了可自定義的線程池。 這些線程池使您可以對諸如線程數&#xff0c;線程重用&#xff0c;調度和線程構造之類的東西進行大量控制。 讓我們回顧一下。 首先&#xff0c;線程池。 讓我們直接進入java.util.concurrent.ExecutorSer…

HTTPPost/AFNetWorking/JSONModel/NSPredicate

一、HTTPPost 1. POST方式發送請求 HTTP協議下默認數據發送請求方法是GET方式&#xff0c;若需要使用POST方法&#xff0c;則需要對發送的請求也就是request對象&#xff0c;進行屬性設置。 步驟如下&#xff1a; > 要發送的請求對象&#xff0c;需要使用可變請求對象 [[NSM…

C語言代碼規范(八)使用const修飾值不允許改變的變量

使用const限定一個變量的值不允許被改變&#xff0c;從而保護被修飾的東西&#xff0c;防止意外&#xff0c;提高程序的可靠性和安全性。

教育小思

父母的時代是“攢錢&#xff0c;買房&#xff0c;生子&#xff0c;終老”&#xff0c;而現在的時代是“教育&#xff0c;創造&#xff0c;傳承&#xff0c;成長”。 改變世界&#xff0c;從教育起步。 傳統教育的不足之處&#xff1a; 1. 學習體驗不佳&#xff0c;學習者被迫…

linux redis客戶端_為什么單線程Redis能那么快?

我們通常說&#xff0c;Redis 是單線程&#xff0c;主要是指 Redis 的網絡 IO 和鍵值對讀寫是由一個線程來完成的&#xff0c;這也是 Redis 對外提供鍵值存儲服務的主要流程。但 Redis 的其他功能&#xff0c;比如持久化、異步刪除、集群數據同步等&#xff0c;其實是由額外的線…

servlet中文亂碼處理

servlet中文亂碼處理 如果是post設置req.setCharacterEncoding("utf-8");如果是get&#xff0c;不去修改服務器配置的情況下new String(name.getBytes("iso-8859-1"),"utf-8")數據庫亂碼?useUnicodetrue&characterEncodingUTF-8轉載于:http…