簡體   English   中英

R 的 Julia 表現不佳

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM