這篇文章主要介紹了我是如何把ruby gem contracts.ruby速度提升10倍的。
contracts.ruby在我項目里用來添加代碼合約(code contracts)到Ruby中??雌饋聿畈欢嗍沁@樣的:
Contract Num, Num => Numdef add(a, b) a + bend
只要add方法被調用,參數和返回值都會被檢查。
20秒
本周末,我對該庫進行了測試,發現其性能非常糟:
這是在隨機輸入下,運行1000次以后的結果。
所以,當給一個函數加入合約功能后,運行速度明顯下降(約40倍這樣),對此,我進行了深入的研究。
8秒
我取得了較大的進展,當傳遞合約時,我調用success_callback函數,該函數是個空函數,下面是這個函數的整個定義:
def self.success_callback(data)end
原來函數調用在Ruby中是非常昂貴的,僅刪除這個調用,就節省了8秒鐘:
刪除其它一些附件函數的調用,時間花費開始從9.84-> 9.59-> 8.01秒,該庫的速度馬上提升到以前的兩倍了。
現在,事情變的有點復雜了。
5.93秒
這里有許多年種定義一個合約的方式:匿名(lambdas)、類 (classes)、簡單舊數據(plain ol' values)等。 我有個很長的case語句,用來檢測合約的類型。在此合約類型基礎之上,我可以做不同的事情。通過把它改為if語句,我節約了一些時間,但每次調用這個函數時,我仍然耗費了不必要的時間在仔細檢查這個判定樹上面:
if contract.is_a?(Class) # check argelsif contract.is_a?(Hash) # check arg...
當定義合約和構建lambda時,對樹只做一次檢查:
if contract.is_a?(Class) lambda { |arg| # check arg }elsif contract.is_a?(Hash) lambda { |arg| # check arg }
然后,我將完全繞過邏輯分支,通過將參數傳遞給預計算的lambda來進行驗證,這樣就節約了1.2秒時間。
預計算一些其它的If語句,差不多又節省了1秒時間:
5.09秒
將.zip轉換為.times又為我節省了1秒時間:
結果證明:
args.zip(contracts).each do |arg, contract|
上面的代碼要比下面這個慢:
args.each_with_index do |arg, i|
要比下面這個更慢:
新聞熱點
疑難解答