2012-04-11 51 views
31

我最近發現可以使用編譯器軟件包(我在a recent blog post中總結了關於此主題的研究結果)使用JIT(及時)編譯R。與R一起使用JIT的可能缺點?

一個我提出的問題是:

有什麼陷阱?這聽起來太好了,只是把一行 的代碼,就是這樣。

環顧四周後,我發現一個可能的問題與JIT的「啓動」時間有關。但在使用JIT時是否還有其他問題需要注意?

我想R的環境架構會有一些限制,但我想不出一個簡單的例子來說明問題,任何建議或紅旗都會有很大的幫助嗎?

+0

我不知道有關性能命中,但(比最初的彙編(也許增加了內存使用情況)除外)「注:沒有明顯的約束力」的消息往往可以壓倒一個新手(例如,如果使用GGPLOT2),並能甩開tab-complete(至少,他們是爲我) – mweylandt 2012-04-11 13:59:30

+0

嗨mweylandt。你碰巧知道錯誤按摩的含義是什麼? – 2012-04-11 14:48:06

+4

當我創建新版本時,我一直在將'ByteCompile:true'放在我的軟件包的DESCRIPTION文件中,它似乎可以正常工作。我做了一個小測試''http:// www.johnmyleswhite.com/notebook/2012/03/31/julia-i-love-you/comment-page-1 /#comment-19522'和字節編譯版本' fib2c'的速度比普通的fib2c快4倍。在某些情況下,即使沒有字節編譯,R也已經很快(例如,使用C下面的高度向量化代碼),並且在那些情況下顯然沒有什麼機會加速 - 它主要用於慢R代碼。 – 2012-04-11 14:52:53

回答

4

上面給出的rpart例如,似乎不再是一個問題:

library("rpart") 
fo = function() { 
    for(i in 1:500){ 
    rpart(Kyphosis ~ Age + Number + Start, data=kyphosis) 
    } 
} system.time(fo()) 
# user system elapsed 
# 1.212 0.000 1.206 
compiler::enableJIT(3) 
# [1] 3 
system.time(fo()) 
# user system elapsed 
# 1.212 0.000 1.210 

我還嘗試了一些其它實例中,諸如

  • 生長的載體;
  • 這只是各地mean

的包裝雖然我並不總是得到一個加速的函數,我從來沒有遇到一個顯著放緩。


R> sessionInfo() 
R version 3.3.0 (2016-05-03) 
Platform: x86_64-pc-linux-gnu (64-bit) 
Running under: Ubuntu 16.04 LTS 
11

與rpart包一個簡單的測試的輸出可能是不建議在所有情況下使用enableJIT:

library(rpart) 
fo <- function() for(i in 1:500){rpart(Kyphosis ~ Age + Number + Start, data=kyphosis)} 
system.time(fo()) 
#User  System verstrichen 
#2.11  0.00  2.11 

require(compiler) 
enableJIT(3) 
system.time(fo()) 
#User  System verstrichen 
#35.46  0.00  35.60 

任何explanantion?

+1

這很奇怪,所以關於在fo編譯循環的一些事情正在引發一個問題。如果你平時編譯它不會發生。 http://ideone.com/Nu8IZ,注意rpart已經被字節編譯。 – Hansi 2012-04-13 09:31:27

+7

編譯需要半分鐘:我看到相同的(2.8 s - 42.6 s),但是接着做system.time(fo())只需要2.6 s。 – cbeleites 2012-04-13 13:05:02

+1

我懷疑這是一個意外的行爲... – 2012-04-14 07:06:57

-2

繼續上一個答案,實驗顯示問題是不是與循環的編譯,它是與編譯閉包。 [enableJIT(0)或enableJIT(1)使代碼保持快速,enableJIT(2)顯着降低速度,並且啓用JIT(3)比前一個選項稍快(但仍然非常慢)]。與Hansi的評論相反,cmpfun將執行速度放慢到了相似的程度。

+0

似乎很清楚,不同結果的原因是R.的不同版本。使用R 3.3。2今天,無論i是0,1,2還是3,enableJIT(i)都給出完全相同的時間(1.21秒)。我將解釋給其他人,但是給我的消息是它沒有任何優化現在用戶的幫助。 – Elroch 2017-01-28 00:09:45

0

原則,一旦字節代碼被編譯和裝載,它應始終速度至少解釋爲原來的AST解釋。一些代碼會受益於大的加速,這通常是代碼有很多標量操作和循環,其中大部分時間花在R解釋上(我已經看到了10倍加速的例子,但任意微基準確實可以根據需要誇大這個)。一些代碼將以相同的速度運行,這通常是代碼向量化的,因此幾乎沒有時間解釋。現在,編譯本身可能會很慢。因此,即時編譯器現在不會編譯函數,因爲它猜測它不會得到回報(並且啓發式會隨着時間的推移而變化,這已經在3.4.x中了)。啓發式並不總是這樣猜測,所以在編譯沒有成功的情況下可能會出現這種情況。典型的問題模式是代碼生成,代碼修改以及對閉包中捕獲的環境綁定的操縱。所以在運行時編譯成本不支付(多次),至少是提前知道代碼

包可以字節編譯安裝時。這是R的開發版本中的默認設置。雖然編譯代碼的加載比編譯代碼快很多,但在某些情況下,可能會加載甚至不會執行的代碼,所以實際上可能會有開銷,但總體上預編譯是有益的。最近一些GC的參數已經被調整,以降低加載代碼的成本,這些代碼將不會被執行。

我的包作家的建議是使用默認設置(即時編譯現在在默認情況下發布的版本中,字節編譯在安裝時是現在的開發版本)。如果您發現字節碼編譯器性能不佳的示例,請提交一個錯誤報告(我還在前面的版本中看到過一個涉及rpart的案例)。我建議不要代碼生成和代碼操作,特別是在熱循環中。這包括定義閉包,在由閉包捕獲的環境中刪除和插入綁定。絕對不應該在熱循環中做eval(parse(text=(並且這已經很糟糕,沒有字節編譯)。動態使用分支比生成新的閉包(沒有分支)總是更好。此外,最好用循環編寫代碼,而不是動態生成具有巨大表達式(無循環)的代碼。現在使用字節碼編譯器,現在通常可以編寫在R中以標量運行的循環(性能不會像以前那麼糟糕,所以如果沒有切換到性能關鍵部件的C,通常可以離開) 。

相關問題