ランタイム・パフォーマンス

GraalVMは、長時間実行されるRコードを最適化します。Rコードのランタイム動作とGraalVMランタイムで採用されている動的コンパイルに基づく投機的最適化では、R言語のダイナミズムと複雑さによって生じる抽象化ペナルティのほとんどを排除できます。

大規模なマトリックスの相互情報量を計算する次の例のアルゴリズムを調べてみましょう:

x <- matrix(runif(1000000), 1000, 1000)
mutual_R <- function(joint_dist) {
 joint_dist <- joint_dist/sum(joint_dist)
 mutual_information <- 0
 num_rows <- nrow(joint_dist)
 num_cols <- ncol(joint_dist)
 colsums <- colSums(joint_dist)
 rowsums <- rowSums(joint_dist)
 for(i in seq_along(1:num_rows)){
  for(j in seq_along(1:num_cols)){
   temp <- log((joint_dist[i,j]/(colsums[j]*rowsums[i])))
   if(!is.finite(temp)){
    temp = 0
   }
   mutual_information <-
    mutual_information + joint_dist[i,j] * temp
  }
 }
 mutual_information
}
system.time(mutual_R(x))
#   user  system elapsed
#  1.321   0.010   1.279

このようなアルゴリズムを効率的に実行するには、通常、C/C++コードが必要です:1

if (!require('RcppArmadillo')) {
    install.packages('RcppArmadillo')
    library(RcppArmadillo)
}
library(Rcpp)
sourceCpp("r_mutual.cpp")
x <- matrix(runif(1000000), 1000, 1000)
system.time(mutual_cpp(x))
#   user  system elapsed
#  0.037   0.003   0.040

(r_mutual.cppを使用しています。)

ただし、数回の反復の後、GraalVMではRコードを十分に効率的に実行できるため、C/C++のパフォーマンス上の利点はごくわずかです:

system.time(mutual_R(x))
#   user  system elapsed
#  0.063   0.001   0.077

GraalVM Rランタイムは主に、長時間実行されるアプリケーションを対象としています。したがって、ピーク・パフォーマンスは通常、ウォームアップ期間の後にのみ達成されます。Javaクラスのロードおよびコンパイルによるオーバーヘッドのため、現時点ではGNU Rよりも起動に時間がかかりますが、将来のリリースにはRのネイティブ・イメージが含まれ、起動時間が短縮されると思われます。




1 この例を初めて実行するときには、RcppArmadilloパッケージがインストールされ、これに数分かかる場合があります。この例は、GraalVMのRランタイムとGNU Rの両方で実行できます。