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