[英]Poor Julia performance from R
我一直在研究使用 Julia 替代 C++ 作為輔助語言來加速 R 腳本的想法。 因此,我正在編寫一個簡短的腳本來比較三種語言的性能,使用 R 腳本作為下面復制的主要文件:
# Loading packages
library(Rcpp)
library(RcppArmadillo)
library(JuliaConnectoR)
library(microbenchmark)
# Generating data
set.seed(9392927)
N <- 1e3L
x1 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
x2 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
# Defining functions
mySolveR <- function(m) {
return(solve(m))
}
myProdR <- function(m1, m2) {
return(m1 %*% m2)
}
cppFunction("
arma::mat mySolveCpp(arma::mat m) {
return(inv(m));
}",
depends = "RcppArmadillo"
)
cppFunction("
arma::mat myProdCpp(arma::mat m1, arma::mat m2) {
return(m1 * m2);
}",
depends = "RcppArmadillo"
)
mySolveJulia <- juliaEval("
function mySolveJulia(m)
return(inv(m))
end"
)
#> Starting Julia ...
myProdJulia <- juliaEval("
function myProdJulia(m1, m2)
return(m1 * m2)
end"
)
# Benchmarking
reps <- 10L
print(
microbenchmark(
mySolveR(x1), mySolveCpp(x1), mySolveJulia(x1),
times = reps
)
)
#> Unit: milliseconds
#> expr min lq mean median uq
#> mySolveR(x1) 41.53371 44.03872 186.4527 59.43063 465.0983
#> mySolveCpp(x1) 49.42867 72.63049 240.0839 81.16933 440.1176
#> mySolveJulia(x1) 3856.43147 3897.36013 5112.4448 5376.94133 6026.1369
#> max neval cld
#> 528.8838 10 a
#> 576.2336 10 a
#> 6220.1093 10 b
print(
microbenchmark(
myProdR(x1, x2), myProdCpp(x1, x2), myProdJulia(x1, x2),
times = reps
)
)
#> Unit: milliseconds
#> expr min lq mean median uq
#> myProdR(x1, x2) 20.10827 23.50031 27.69398 25.18961 32.51614
#> myProdCpp(x1, x2) 25.59671 29.59619 38.32624 31.83016 49.33401
#> myProdJulia(x1, x2) 5698.10212 6135.62034 6353.71632 6412.39110 6487.12516
#> max neval cld
#> 39.89406 10 a
#> 57.82850 10 a
#> 7243.81362 10 b
創建於 2022-12-14,使用reprex v2.0.2
在這個階段,我並不擔心哪種語言表現最好,我什至認為如此簡單的功能會以相同的數量級表現,但我很驚訝 Julia 表現如此糟糕。
我唯一的猜測與不必要的代碼重新編譯或 Julia 實例啟動有關,但使用startJuliaServer()
/ stopJulia()
無濟於事,將函數隔離在它們自己的源文件中也無濟於事。 我錯過了什么?
我沒有使用過 JuliaConnectoR,但我測試了 JuliaCall:
設置(也許可以用更簡潔的方式編寫 - 我剛剛查看了JuliaCall 手冊;您可以查看此處列出的示例包的來源,以查看更精心設計的代碼):
library(JuliaCall)
library(microbenchmark)
julia <- julia_setup()
set.seed(9392927)
N <- 1e3L
x1 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
x2 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
mySolveR <- function(m) {
return(solve(m))
}
myProdR <- function(m1, m2) {
return(m1 %*% m2)
}
julia_eval("
function mySolveJulia(m)
return(inv(m))
end")
mySolveJulia <- function(m) {
julia_call("mySolveJulia", m)
}
julia_eval("
function myProdJulia(m1, m2)
return(m1 * m2)
end")
myProdJulia <- function(m1, m2) {
julia_call("myProdJulia", m1, m2)
}
和基准:
> reps <- 10L
> print(
+ microbenchmark(
+ mySolveR(x1), mySolveJulia(x1),
+ times = reps
+ )
+ )
Unit: milliseconds
expr min lq mean median uq max neval
mySolveR(x1) 516.1157 517.4391 520.98363 520.60125 522.0257 531.6278 10
mySolveJulia(x1) 50.8057 52.9256 73.29882 55.88265 67.5247 167.2614 10
> print(
+ microbenchmark(
+ myProdR(x1, x2), myProdJulia(x1, x2),
+ times = reps
+ )
+ )
Unit: milliseconds
expr min lq mean median uq max neval
myProdR(x1, x2) 499.3371 509.4347 526.256 523.8693 532.5146 562.5851 10
myProdJulia(x1, x2) 331.7332 332.5554 354.807 337.0199 351.8239 492.5586 10
令人驚訝的是,我對 R 的計時比你高得多(但這可能是運行測試的機器的差異)。
此外,如果您想查看在數據傳輸上花費了多少時間,可以使用 Julia 中的基准測試:
julia> using Distributions
julia> using BenchmarkTools
julia> x1 = rand(Poisson(10), 1000, 1000);
julia> x2 = rand(Poisson(10), 1000, 1000);
julia> @benchmark inv($x1)
BenchmarkTools.Trial: 168 samples with 1 evaluation.
Range (min … max): 25.273 ms … 55.200 ms ┊ GC (min … max): 0.00% … 9.74%
Time (median): 28.539 ms ┊ GC (median): 0.00%
Time (mean ± σ): 29.815 ms ± 4.172 ms ┊ GC (mean ± σ): 2.40% ± 3.90%
▃█▂▃▁▂ ▄▆▁▄ ▂
▃▁███████▇████▇▄█▅▅█▃▃▁▁▅▄▄▁▁▁▄▃▄▅▃▁▃▁▁▃▃▇▁▁▁▃▁▃▁▄▁▁▁▁▃▁▃▁▃ ▃
25.3 ms Histogram: frequency by time 42.1 ms <
Memory estimate: 15.76 MiB, allocs estimate: 7.
julia> @benchmark $x1 * $x2
BenchmarkTools.Trial: 17 samples with 1 evaluation.
Range (min … max): 293.104 ms … 321.683 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 309.639 ms ┊ GC (median): 0.00%
Time (mean ± σ): 309.618 ms ± 8.177 ms ┊ GC (mean ± σ): 0.11% ± 0.31%
▁ ▁ ▁ ▁▁ ▁ ▁ ▁█ ▁ ▁ ▁ ▁ ▁▁ ▁
█▁▁▁▁▁▁▁▁▁▁▁▁█▁▁▁█▁▁██▁▁▁▁▁█▁▁▁▁█▁██▁█▁▁▁▁█▁▁▁▁▁▁▁▁█▁█▁▁▁██▁█ ▁
293 ms Histogram: frequency by time 322 ms <
Memory estimate: 7.66 MiB, allocs estimate: 5.
使用您的代碼,您可以在每次調用時重新啟動 Julia。 下面是我會怎么做。 但與@BogumilKaminksi 相反,我沒有得到與 Julia 更好的時機。 我不知道為什么。
library(JuliaConnectoR)
juliaModule <- "
module myJuliaModule
export mySolveJulia
export myProdJulia
function mySolveJulia(m)
return inv(m)
end
function myProdJulia(m1, m2)
return m1 * m2
end
end
"
. <- juliaEval(juliaModule)
myJuliaModule <- juliaImport(".myJuliaModule", all = FALSE)
mySolveJulia <- myJuliaModule$mySolveJulia
mySolveR <- function(x) solve(x)
set.seed(9392927)
N <- 1e3L
x1 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
x2 <- matrix(rpois(N ^ 2L, lambda = 10L), N)
reps <- 10L
microbenchmark::microbenchmark(
mySolveR(x1),
mySolveJulia(x1),
times = reps
)
stopJulia()
根據建議,我添加了一個鏈接,指向我在 GitHub 上與 JuliaConnectoR 的一位作者的對話: https ://github.com/stefan-m-lenz/JuliaConnectoR/issues/21
他回復的要點是:
為了與 Julia 通信,JuliaConnectoR 使用 TCP。 這需要 IO 操作,這會增加執行時間。 [...] IO 操作比在內存上執行這些高度優化的函數要慢得多。 [...] 使用 JuliaConnectoR,您通過 TCP 為 IO 增加了一些通信開銷。 所以你不能用它來通過測量 R 中整個操作的執行時間來衡量 Julia 的性能本身。
所以我的實際解決方案是實現我的更現實的函數示例(如預期的那樣,我讓 Julia 和 C++ 勝過 R)並使用 OP 中更簡單的函數作為反例(即,當不值得翻譯 R 代碼時).
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.