［翻訳］R6 vignette: R6と参照クラスの性能テスト

この文書は，Winston Chang によるRパッケージ [`R6`](https://cran.r-project.org/package=R6) (version 2.2.0) のビネット ["R6 and Reference class performance tests"](https://cran.r-project.org/package=R6/vignettes/Performance.html) の日本語訳です．

License: [MIT](https://opensource.org/licenses/MIT)

**関連文書**

- [R6クラス入門](http://qiita.com/nakamichi/items/d724468e0f716d7a174c)
- [移植可能R6クラスと移植不可R6クラス](http://qiita.com/nakamichi/items/b1240935894c2ca72c98)
- [R6オブジェクトのメソッドのデバッグ](http://qiita.com/nakamichi/items/97551ebc6bdfe1119cba)
- [R6と参照クラスの性能テスト](http://qiita.com/nakamichi/items/1841e7450a5621fa7142)

----



<!-- This document compares the memory costs and speed of R's reference classes against R6 classes and simple environments. For must uses, R6 and reference classes have comparable features, but as we'll see, R6 classes are faster and lighter weight. -->

この文書では，Rの参照クラスのメモリ負荷と速度を，R6クラスと単純な環境に対して比較します．大抵の用途においてR6と参照クラスは同等の機能を持っていますが，これから見るようにR6クラスの方が高速で軽量です．

<!-- This document tests reference classes against R6 classes (in many variations), as well as against very simple reference objects: environments created by functino calls. -->

この文書では，参照クラスをR6クラスに対して（様々なバリエーションで）テストします．単純な参照オブジェクト（関数呼び出しで作成した環境）に対してもテストします．

*****

<!-- First we'll load some packages which will be used below: -->

まずは以下で使用するパッケージをロードしましょう．


```r
library(microbenchmark)
options(microbenchmark.unit = "us")
library(pryr)  # object_size 関数用
library(R6)
```




*****

<!-- Class definitions -->
<!-- ================= -->

クラス定義
=================

<!-- We'll start by defining a number of classes or class-like entities, using reference classes, R6 classes, and simple environments that are created directly by functions. There are a number of options for R6 that can affect the size of the resulting objects, so we will use a number of variants. These classes will be used for the speed and memory tests that follow. This is a lot of boring code, so you may want to skip ahead to the results. -->

参照クラスやR6，それから単純な環境を用いて，多数のクラスやクラスに類似したものを定義することから始めます．これらは関数によって直接作成できます．R6にはオブジェクトのサイズに影響する多くのオプションがあるので，様々なバリエーションを使うことにします．これらのクラスは以下で速度とメモリのテストに使用されます．退屈なコードばかりなのでテスト結果まで飛ばしてもかまいません．

<!-- All of these classes have the same basic characteristics: -->

これらのクラスはすべて共通の特性を備えています．

<!-- * A field named `x` that contains a number. -->
<!-- * An way of initializing the value of `x`. -->
<!-- * A method named `getx` for retrieving the value of `x`. -->
<!-- * A method named `inc` for incrementing the value of `x`. -->

* 数値を含む`x`という名前のフィールド
* `x`の値の初期化方法
* `x`の値を取得するための`getx`という名前のメソッド
* `x`の値をインクリメントするための`inc`という名前のメソッド

<!-- The fields and methods are accessed with the `$` operator, so if we have an object named `obj`, we could use `obj$x` or `obj$getx()`. -->

これらのフィールドやメソッドには`$`演算子でアクセスします．仮に`obj`という名前のオブジェクトがあれば，`obj$x`や`obj$getx()`が使えるでしょう．

<!-- ## R reference class -->

## Rの参照クラス


```r
RC <- setRefClass("RC",
  fields = list(x = "numeric"),
  methods = list(
    initialize = function(x = 1) .self$x <- x,
    getx = function() x,
    inc = function(n = 1) x <<- x + n
  )
)
```

<!-- In reference classes, the binding that points back to the object is named `.self`. Within a method, assignment can be done by using `.self`, as in `.self$x <- 10`, or by using `<<-`, as in `x <<- 10`. -->

参照クラスでは，オブジェクト自身を指し返すように束縛されている名前を`.self`といいます．メソッド内での代入は`.self`を用いて`.self$x <- 10`のように行えます．あるいは`<<-`を用いて`x <<- 10`のようにもできます．

<!-- To create an object, simply call `$new()` on the class: -->

オブジェクトを作成するには，単にクラスの`$new()`を呼び出します．


```r
RC$new()
#> Reference class object of class "RC"
#> Field "x":
#> [1] 1
```

<!-- ## R6 class -->

## R6クラス

<!-- Creating an R6 class is similar to the reference class, except that there's no need to separate the fields and methods, and you can't specify the types of the fields. -->

R6クラスの作成方法は参照クラスと同様です．ただしフィールドとメソッドを分ける必要がなく，またフィールドの型は指定できません


```r
R6 <- R6Class("R6",
  public = list(
    x = NULL,
    initialize = function(x = 1) self$x <- x,
    getx = function() self$x,
    inc = function(n = 1) self$x <- x + n
  )
)
```

<!-- Whereas reference classes use `.self`, R6 classes use `self` (without the leading period). As with reference classes, objects are instantiated by calling `$new()`: -->

参照クラスでは`.self`を使いますが，R6クラスでは（先頭のピリオドのない）`self`を使います．参照クラスと同様，オブジェクトは`$new()`を呼んでインスタンス化します．


```r
R6$new()
#> <R6>
#>   Public:
#>     clone: function (deep = FALSE) 
#>     getx: function () 
#>     inc: function (n = 1) 
#>     initialize: function (x = 1) 
#>     x: 1
```

<!-- An R6 object essentially just a set of environments structured in a particular way. The fields and methods for an R6 object have bindings (that is, they have names) in the *public environment*. There is also have a separate environment which is the *enclosing environment* for methods (they "run in" an environment that contains a binding named `self`, which is simply a reference to the public environment). -->

本質的には，R6オブジェクトは環境の集まりをあるやり方で構造化したものにすぎません．R6オブジェクトのフィールドとメソッドは**パブリック環境**に束縛されています（すなわち，パブリック環境に名前を持ちます）．これとは別にメソッドの**エンクロージング環境**があります．（メソッドは`self`という名前が束縛された環境で実行されます．`self`はパブリック環境への単なる参照です．）


<!-- ## R6 class, without class attribute -->

## R6クラス（`class`属性なし）

<!-- By default, a class attribute is added to R6 objects. This attribute adds a slight performance penalty because R will attempt to use S3 dispatch when using `$` on the object. -->

デフォルトでは，R6オブジェクトには`class`属性が付加されます．この属性の付加によって，わずかに性能が損なわれます．なぜかというと，オブジェクトに対して`$`が使用されると，RはS3のディスパッチを試みるからです，

<!-- It's possible generate objects without the class attribute, by using `class=FALSE`: -->

`class=FALSE`とすれば`class`属性なしのオブジェクトを生成することができます．


```r
R6NoClass <- R6Class("R6NoClass",
  class = FALSE,
  public = list(
    x = NULL,
    initialize = function(x = 1) self$x <- x,
    getx = function() self$x,
    inc = function(n = 1) self$x <- self$x + n
  )
)
```

<!-- Note that without the class attribute, S3 method dispatch on the objects is not possible. -->

`class`属性がないと，オブジェクトに対してS3メソッドのディスパッチができないことに注意してください．


<!-- ## R6 class, non-portable -->

## R6クラス（移植不可）

<!-- By default, R6 objects are *portable*. This means that inheritance can be in classes that are in different packages. However, it also requires the use of `self$` and `private$` to access members, and this incurs a small performance penalty. -->

デフォルトでは，R6オブジェクトは**移植可能**です．つまり異なるパッケージにあるクラス間で継承ができます．しかし，このためにメンバにアクセスするのに`self$`や`private$`の使用も必要となり，性能では少々損失を被ることになります．

<!-- If `portable=FALSE` is used, members can be accessed without using `self$`, and assignment can be done with `<<-`: -->

`portable=FLASE`とすれば，メンバには`self$`なしでアクセスでき，代入は`<<-`で行えます．


```r
R6NonPortable <- R6Class("R6NonPortable",
  portable = FALSE,
  public = list(
    x = NULL,
    initialize = function(value = 1) x <<- value,
    getx = function() x,
    inc = function(n = 1) x <<- x + n
  )
)
```

<!-- ## R6 class, with `cloneable=FALSE` -->


## R6クラス（`cloneable=FALSE`）

<!-- By default, R6 objects have a `clone()` method, which is a fairly large function. If you do not need this feature, you can save some memory by using `cloneable=FALSE`. -->

デフォルトではR6オブジェクトは`clone()`メソッドを持ちますが，これはかなり大きな関数です．この機能が不要な場合には`cloneable=FALSE`とすればメモリを節約できます．


```r
R6NonCloneable <- R6Class("R6NonCloneable",
  cloneable = FALSE,
  public = list(
    x = NULL,
    initialize = function(x = 1) self$x <- x,
    getx = function() self$x,
    inc = function(n = 1) self$x <- self$x + n
  )
)
```


<!-- ## R6 class, without class attribute, non-portable, and non-cloneable -->

## R6クラス（`class`属性なし，移植不可，クローン不可）

<!-- For comparison, we'll use a an R6 class that is without a class attribute, non-portable, and non-cloneable. This is the most stripped-down we can make an R6 object. -->

比較のために，`class`属性を持たず，移植不可で，クローン不可能なR6クラスを使います．これは余計なものを可能な限り取り除いたR6オブジェクトです．


```r
R6Bare <- R6Class("R6Bare",
  portable = FALSE,
  class = FALSE,
  cloneable = FALSE,
  public = list(
    x = NULL,
    initialize = function(value = 1) x <<- value,
    getx = function() x,
    inc = function(n = 1) x <<- x + n
  )
)
```


<!-- ## R6 class, with public and private members -->

## R6クラス（パブリックメンバとプライベートメンバあり）

<!-- This variant has public and private members. -->

これはパブリックメンバとプライベートメンバを持つバージョンです．


```r
R6Private <- R6Class("R6Private",
  private = list(x = NULL),
  public = list(
    initialize = function(x = 1) private$x <- x,
    getx = function() private$x,
    inc = function(n = 1) private$x <- private$x + n
  )
)
```

<!-- Instead of a single `self` object which refers to all items in an object, these objects have `self` (which refers to the public items) and `private`. -->

このR6オブジェクトは，`self`だけでオブジェクト内の全ての項目にアクセスすることはできず，（パブリックな項目を指す）`self`と`private`を持ちます．



```r
R6Private$new()
#> <R6Private>
#>   Public:
#>     clone: function (deep = FALSE) 
#>     getx: function () 
#>     inc: function (n = 1) 
#>     initialize: function (x = 1) 
#>   Private:
#>     x: 1
```


<!-- ## R6 class, with public and private, no class attribute, non-portable, and non-cloneable -->

## R6クラス（パブリックメンバとプライベートメンバあり，`class`属性なし，移植不可，クローン不可）

<!-- For comparison, we'll add a version that is without a class attribute, non-portable, and non-cloneable. -->

比較のため，`class`属性を持たず，移植不可で，クローン不可能なバージョンを加えます．


```r
R6PrivateBare <- R6Class("R6PrivateBare",
  portable = FALSE,
  class = FALSE,
  cloneable = FALSE,
  private = list(x = NULL),
  public = list(
    initialize = function(x = 1) private$x <- x,
    getx = function() x,
    inc = function(n = 1) x <<- x + n
  )
)
```


<!-- ## Environment created by a function call, with class attribute -->

## 環境（関数呼び出しで作成，`class`属性あり）

<!-- In R, environments are passed by reference. A simple way to create an object that's passed by reference is to use the environment created by the invocation of a function. The function below captures that environment, attaches a class to it, and returns it: -->

Rでは，環境は参照渡しされます．参照渡しのオブジェクトを作るには，関数実行によって作成される環境を使うのが簡単です．


```r
FunctionEnvClass <- function(x = 1) {
  inc <- function(n = 1) x <<- x + n
  getx <- function() x
  self <- environment()
  class(self) <- "FunctionEnvClass"
  self
}
```

<!-- Even though `x` isn't declared in the function body, it gets captured because it's an argument to the function. -->

`x`は関数の本体では宣言されていませんが，関数の引数になっているので，環境に捕捉されます．


```r
ls(FunctionEnvClass())
#> [1] "getx" "inc"  "self" "x"
```

<!-- Objects created this way are very similar to those created by `R6` generator we created above. -->

こうして作成したオブジェクトは，上で作成した`R6`のジェネレータによく似ています．


<!-- ## Environment created by a function call, without class attribute -->

## 環境（関数呼び出しで作成，`class`属性なし）

<!-- We can make an even simpler type of reference object to the previous one, by not having a a class attribute, and not having `self` object: -->

`class`属性をなくして，`self`オブジェクトもなくすことで，前述のものよりさらに単純な種類の参照オブジェクトを作ることができます．


```r
FunctionEnvNoClass <- function(x = 1) {
  inc <- function(n = 1) x <<- x + n
  getx <- function() x
  environment()
}
```

<!-- This is simply an environment with some objects in it. -->

これはいくつかのオブジェクトを含む単なる環境です．


```r
ls(FunctionEnvNoClass())
#> [1] "getx" "inc"  "x"
```

*****

<!-- Tests -->
<!-- ===== -->

テスト
=====

<!-- For all the timings using `microbenchmark()`, the results are reported in microseconds, and the most useful value is probably the median column. -->

`microbenchmark()`による時間の計測結果はすべてマイクロ秒で報告されます．結果の中で最も役立つのはたぶん中央値でしょう．

<!-- ## Memory footprint -->

## メモリ負荷



<!-- How much memory does a single instance of each object take, and how much memory does each additional object take? We'll use the functions `obj_size` and `obj_sizes` (shown at the bottom of this document) to calculate the sizes. -->

各オブジェクトのひとつのインスタンスはどれだけメモリを使用するのでしょうか．また，オブジェクトを追加するとどれだけメモリを使用するのでしょうか．オブジェクトのサイズを計算するには`obj_size()`と`obj_sizes()`という関数を使います（関数定義はこの文書の最下部にあります）．

<!-- Sizes of each type of object, in bytes: -->

それぞれの種類のオブジェクトのサイズ（バイト単位）は以下の通りです[^test_obj_size]．


```r
sizes <- obj_sizes(
  RC$new(),
  R6$new(),
  R6NoClass$new(),
  R6NonPortable$new(),
  R6NonCloneable$new(),
  R6Bare$new(),
  R6Private$new(),
  R6PrivateBare$new(),
  FunctionEnvClass(),
  FunctionEnvNoClass()
)
sizes
#>                         one incremental
#> RC$new()             461168        1368
#> R6$new()              56608        1008
#> R6NoClass$new()       57312         896
#> R6NonPortable$new()   56200         952
#> R6NonCloneable$new()  13608         896
#> R6Bare$new()          12800         728
#> R6Private$new()       57512        1120
#> R6PrivateBare$new()   13808         840
#> FunctionEnvClass()    10696         624
#> FunctionEnvNoClass()   9272         512
```

<!-- The results are plotted below. Note that the plots have very different x scales. -->

結果を以下にプロットしました．プロットのx軸のスケールが大きく異なっていることに注意してください．

![unnamed-chunk-21-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/67c188ab-5a64-f8a5-c6e3-7cd9885ef6fd.png) ![unnamed-chunk-21-2.png](https://qiita-image-store.s3.amazonaws.com/0/58917/21ae4ec9-6e51-bfc2-3910-7e03d3536acd.png)

<!-- Some preliminary observations about the first instance of various classes: Using a reference class consumes a large amount of memory. For R6 objects, the option with the largest impact is `cloneable`: not having the `clone()` method saves around 40 kB of memory. -->

色々なクラスのひとつめのインスタンスについて，まず以下のことが分かります．参照クラスは大量のメモリを消費すること．R6オブジェクトに対して最も影響のあるオプションは`cloneable`であること．`clone()`メソッドなしだとメモリが約40kB節約されています．

<!-- For subsequent instances of these classes, there isn't nearly as much difference between the different kinds. -->

各クラスの追加されたインスタンスについては，異なる種類のクラスの間で，ひとつめのインスタンスほどの違いはありません．

<!-- It appeared that using a reference class takes up a huge amount of memory, but much of that is shared between reference classes. Adding an object from a different reference class doesn't require much more memory --- around 38KB: -->

参照クラスは大量のメモリを占めるように見えましたが，その大部分は複数の参照クラスで共有されるものです．別の参照クラスのオブジェクトを追加しても，それほど多くのメモリは必要としません（約38kB）．



```r
RC2 <- setRefClass("RC2",
  fields = list(x = "numeric"),
  methods = list(
    initialize = function(x = 2) .self$x <<- x,
    inc = function(n = 2) x <<- x * n
  )
)

# RCオブジェクトに加えて，新しく作ったRC2オブジェクトのサイズを計算
as.numeric(object_size(RC$new(), RC2$new()) - object_size(RC$new()))
#> [1] 37344
```
<!-- # Calcualte the size of a new RC2 object, over and above an RC object -->


<!-- ## Object instantiation speed -->

## オブジェクトのインスタンス化の速度

<!-- How much time does it take to create one of these objects? This shows the median time, in microseconds: -->

各オブジェクトを作成するのには，どれくらい時間がかかるのでしょうか．以下に，かかった時間の中央値をマイクロ秒で示します．


```r
# microbenchmarkの結果から中央値を取り出す関数
mb_summary <- function(x) {
  res <- summary(x, unit="us")
  data.frame(name = res$expr, median = res$median)
}

speed <- microbenchmark(
  RC$new(),
  R6$new(),
  R6NoClass$new(),
  R6NonPortable$new(),
  R6NonCloneable$new(),
  R6Bare$new(),
  R6Private$new(),
  R6PrivateBare$new(),
  FunctionEnvClass(),
  FunctionEnvNoClass()
)
speed <- mb_summary(speed)
speed
#>                    name   median
#> 1              RC$new() 279.6430
#> 2              R6$new()  49.8450
#> 3       R6NoClass$new()  44.8755
#> 4   R6NonPortable$new()  48.0375
#> 5  R6NonCloneable$new()  48.0385
#> 6          R6Bare$new()  39.1540
#> 7       R6Private$new()  67.6145
#> 8   R6PrivateBare$new()  60.5370
#> 9    FunctionEnvClass()   3.0120
#> 10 FunctionEnvNoClass()   1.5060
```
<!-- # Function to extract the medians from microbenchmark results -->

<!-- The plot below shows the median instantiation time. -->

以下のプロットはインスタンス化にかかった時間の中央値を示しています．

![unnamed-chunk-24-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/2be7ef9c-193c-91dc-948b-7452697a7185.png)

<!-- Reference classes are much slower to instantiate than the other types of classes. Instantiating R6 objects is roughly 5 times faster. Creating an environment with a simple function call is another 20-30 times faster. -->

参照クラスは，他の種類のクラスのインスタンス化よりずっと低速です．R6オブジェクトのインスタンス化は，おおよそ5倍高速です．単純な関数呼び出しで環境を作成するのは，さらに20～30倍高速です．


<!-- ## Field access speed -->

## フィールドへのアクセス速度

<!-- How much time does it take to access a field in an object? First we'll make some objects: -->

オブジェクトのフィールドにアクセスするのには，どれだけ時間がかかるのでしょうか．まずオブジェクトを作ります．


```r
rc           <- RC$new()
r6           <- R6$new()
r6noclass    <- R6NoClass$new()
r6noport     <- R6NonPortable$new()
r6noclone    <- R6NonCloneable$new()
r6bare       <- R6Bare$new()
r6priv       <- R6Private$new()
r6priv_bare  <- R6PrivateBare$new()
fun_env      <- FunctionEnvClass()
fun_env_nc   <- FunctionEnvNoClass()
```

<!-- And then get a value from these objects: -->

そして，これらのオブジェクトから値を取得します．


```r
speed <- microbenchmark(
  rc$x,
  r6$x,
  r6noclass$x,
  r6noport$x,
  r6noclone$x,
  r6bare$x,
  r6priv$x,
  r6priv_bare$x,
  fun_env$x,
  fun_env_nc$x
)
#> Warning in microbenchmark(rc$x, r6$x, r6noclass$x, r6noport$x, r6noclone
#> $x, : Could not measure a positive execution time for 30 evaluations.
speed <- mb_summary(speed)
speed
#>             name median
#> 1           rc$x  7.529
#> 2           r6$x  0.904
#> 3    r6noclass$x  0.000
#> 4     r6noport$x  0.904
#> 5    r6noclone$x  0.904
#> 6       r6bare$x  0.000
#> 7       r6priv$x  0.904
#> 8  r6priv_bare$x  0.000
#> 9      fun_env$x  0.904
#> 10  fun_env_nc$x  0.000
```

![unnamed-chunk-27-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/97cda18c-8023-7241-919b-20465ac0ab2d.png)


<!-- Accessing the field of a reference class is much slower than the other methods. -->

参照クラスのフィールドへのアクセスは，他の方法よりもずっと低速です．

<!-- There's also an obvious pattern where accessing the field of an environment (created by R6 or a function call) is slower when there is a class attribute. This is because, for the objects that have a class attribute, R attempts to look up an S3 method for `$`, and this lookup has a performance penalty. We'll see more about this below. -->

また，環境（R6か関数呼び出しで作成したもの）のフィールドへのアクセスには，`class`属性があると遅くなるという明確なパターンがあります．これは，`class`属性を持つオブジェクトに対してRが`$`のS3メソッドを探そうとするのが性能上の損失となるからです．これについては以下でさらに詳しく見ます．

<!-- ## Field setting speed -->

## フィールドの設定速度

<!-- How much time does it take to set the value of a field in an object? -->

オブジェクトのフィールドに値を設定するのにかかる時間はどれくらいでしょうか．


```r
speed <- microbenchmark(
  rc$x <- 4,
  r6$x <- 4,
  r6noclass$x <- 4,
  r6noport$x <- 4,
  r6noclone$x <- 4,
  r6bare$x <- 4,
  # r6priv$x <- 4,         # プライベートフィールドは直接設定できないので，
  # r6priv_nc_np$x <- 4,   # この2つは省略する
  fun_env$x <- 4,
  fun_env_nc$x <- 4
)
speed <- mb_summary(speed)
speed
#>                name  median
#> 1         rc$x <- 4 38.5510
#> 2         r6$x <- 4  1.8070
#> 3  r6noclass$x <- 4  0.6030
#> 4   r6noport$x <- 4  1.8075
#> 5  r6noclone$x <- 4  1.9580
#> 6     r6bare$x <- 4  0.6030
#> 7    fun_env$x <- 4  1.8070
#> 8 fun_env_nc$x <- 4  0.6020
```
<!-- # r6priv$x <- 4,         # Can't set private field directly, -->
<!-- # r6priv_nc_np$x <- 4,   # so we'll skip these two -->


![unnamed-chunk-29-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/98a1ad4e-1b8c-c673-2e1e-3051b0ebb5ae.png)

<!-- Reference classes are significantly slower than the others, again. In this case, there's additional overhead due to type-checking the value. -->

やはり参照クラスは他のものよりはるかに低速です．この場合には，値の型チェックによる追加のオーバーヘッドがあります．

<!-- Once more, the no-class objects are significantly faster than the others, again probably due to attempted S3 dispatch on the `` `$<-` `` function. -->

ここでも`class`属性なしのオブジェクトはそうでないものよりはるかに高速です．これも`` `$<-` ``関数のS3ディスパッチが試みられるのが原因でしょう．

<!-- ## Speed of method call that accesses a field -->


## フィールドにアクセスするメソッド呼び出しの速度

<!-- How much overhead is there when calling a method from one of these objects? All of these `getx()` methods simply return the value of `x` in the object. When necessary, this method uses `self$x` (for R6 classes, when `portable=TRUE`), and in others, it just uses `x` (when `portable=FALSE`, and in reference classes). -->

各オブジェクトのメソッドを呼び出すときのオーバーヘッドはどのくらいでしょうか．どのオブジェクトの`getx()`メソッドも単に`x`の値を返すだけのものです．このメソッドは必要な場合（`portable=TRUE`のR6オブジェクトの場合）には`self$x`を使います．他の場合（`portable=FALSE`のR6と参照クラスの場合）には単に`x`を使います．


```r
speed <- microbenchmark(
  rc$getx(),
  r6$getx(),
  r6noclass$getx(),
  r6noport$getx(),
  r6noclone$getx(),
  r6bare$getx(),
  r6priv$getx(),
  r6priv_bare$getx(),
  fun_env$getx(),
  fun_env_nc$getx()
)
speed <- mb_summary(speed)
speed
#>                  name median
#> 1           rc$getx()  7.529
#> 2           r6$getx()  2.409
#> 3    r6noclass$getx()  0.302
#> 4     r6noport$getx()  1.205
#> 5    r6noclone$getx()  2.410
#> 6       r6bare$getx()  0.301
#> 7       r6priv$getx()  1.506
#> 8  r6priv_bare$getx()  0.301
#> 9      fun_env$getx()  1.204
#> 10  fun_env_nc$getx()  0.301
```

![unnamed-chunk-31-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/35fa2b94-574a-58b7-c7aa-c2193e26d585.png)


<!-- The reference class is the slowest. -->

参照クラスが最も低速です．

<!-- `r6` is also somewhat slower than the others. There are two reasons for this: first, it uses `self$x` which adds some time, and second, it has a class attribute, which slows down the access of both `r6$getx` and `self$x`. -->

`r6`も他のものよりはいくらか低速です．

<!-- One might expect `r6priv` to be the same speed as `r6`, but it is faster. Although accessing `r6priv$getx` is slow because `r6priv` has a class attribute, accessing `private$x` is faster because it does not have a class attribute. -->

`r6priv`は`r6`と同じ速さだろうと思うかもしれませんが，`r6priv`の方が高速です．`r6priv`には`class`属性があるので`r6priv$getx`は遅いのですが，`private`には`class`属性がないので，`private$x`は`self$x`より高速なのです．

<!-- The objects which can access `x` directly (without `self` or `private`) and which lack a class attribute are the fastest. -->

`x`に直接（`self`や`private`なしで）アクセスできて，`class`属性もないオブジェクトが最速です．


<!-- ## Assignment using `self$x <-` vs. `x <<-` -->

## `self$x <-`と`x <<-`による代入

<!-- With reference classes, you can modify fields using the `<<-` operator, or by using the `.self` object. For example, compare the `setx()` methods of these two classes: -->

参照クラスでは，`<<-`演算子か`.self`オブジェクトを用いてフィールドを変更できます．例として，以下の2つのクラスの`setx()`メソッドを比べてみてください．


```r
RCself <- setRefClass("RCself",
  fields = list(x = "numeric"),
  methods = list(
    initialize = function() .self$x <- 1,
    setx = function(n = 2) .self$x <- n
  )
)

RCnoself <- setRefClass("RCnoself",
  fields = list(x = "numeric"),
  methods = list(
    initialize = function() x <<- 1,
    setx = function(n = 2) x <<- n
  )
)
```

<!-- Non-portable R6 classes are similar, except they use `self` instead of `.self`. -->

移植不可R6クラスもこれと同様です．ただし`.self`ではなく`self`を使います．


```r
R6self <- R6Class("R6self",
  portable = FALSE,
  public = list(
    x = 1,
    setx = function(n = 2) self$x <- n
  )
)

R6noself <- R6Class("R6noself",
  portable = FALSE,
  public = list(
    x = 1,
    setx = function(n = 2) x <<- n
  )
)
```



```r
rc_self   <- RCself$new()
rc_noself <- RCnoself$new()
r6_self   <- R6self$new()
r6_noself <- R6noself$new()

speed <- microbenchmark(
  rc_self$setx(),
  rc_noself$setx(),
  r6_self$setx(),
  r6_noself$setx()
)
speed <- mb_summary(speed)
speed
#>               name  median
#> 1   rc_self$setx() 45.7790
#> 2 rc_noself$setx() 28.3105
#> 3   r6_self$setx()  4.8190
#> 4 r6_noself$setx()  2.1080
```

![unnamed-chunk-35-1.png](https://qiita-image-store.s3.amazonaws.com/0/58917/77e44ab9-f38a-6754-b783-59a03df3398b.png)


<!-- For both reference and non-portable R6 classes, assignment using `.self$x <-` is somewhat slower than using `x <<-`. -->

参照クラスと移植不可R6クラスの両方とも，`.self$x <-`による代入は`x <<-`よりやや低速です．

<!-- Bear in mind that, by default, R6 classes are portable, and can't use assignment with `x <<-`. -->

R6クラスはデフォルトでは移植可能なので，`x <<-`での代入はできないということは覚えておいてください．


<!-- ## Overhead from using `$` on objects with a class attribute -->

## `class`属性を持つオブジェクトにおける`$`使用のオーバーヘッド

<!-- There is some overhead when using `$` on an object that has a class attribute. In the test below, we'll create three different kinds of objects: -->

`class`属性を持つオブジェクトで`$`を使うときにはオーバーヘッドが生じます．以下のテストでは3つの異なる種類のオブジェクトを作成します．

<!-- 1. An environment with no class attribute. -->
<!-- 1. An environment with a class `"e2"`, but without a `$.e2` S3 method. -->
<!-- 1. An environment with a class `"e3"`, which has a `$.e3` S3 method that simply returns `NULL`. -->

1. `class`属性を持たない環境
1. `class`属性`"e2"`を持つが，S3メソッド`$.e2`は持たない環境
1. `class`属性`"e3"`を持ち，単にNULLを返すS3メソッド`$.e3`を持つ環境

<!-- Each one of these environments will contain an object `x`. -->

各環境はオブジェクト`x`を含むものとします．


```r
e1 <- new.env(hash = FALSE, parent = emptyenv())
e2 <- new.env(hash = FALSE, parent = emptyenv())
e3 <- new.env(hash = FALSE, parent = emptyenv())

e1$x <- 1
e2$x <- 1
e3$x <- 1

class(e2) <- "e2"
class(e3) <- "e3"

# クラスe3のS3メソッドを定義
`$.e3` <- function(x, name) {
  NULL
}
```
<!-- # Define an S3 method for class e3 -->

<!-- Now we can run timing tests for calling `$` on each type of object. Note that for the `e3` object, the `$` function does nothing --- it simply returns `NULL`. -->

これでそれぞれの種類のオブジェクトに対して，`$`呼び出しの時間計測テストが実行できます．`e3`オブジェクトの`$`関数は何もせず，単に`NULL`返すだけであることに注意してください．


```r
speed <- microbenchmark(
  e1$x,
  e2$x,
  e3$x
)
#> Warning in microbenchmark(e1$x, e2$x, e3$x): Could not measure a positive
#> execution time for 23 evaluations.
speed <- mb_summary(speed)
speed
#>   name median
#> 1 e1$x  0.000
#> 2 e2$x  0.603
#> 3 e3$x  0.603
```

<!-- Using `$` on `e2` and `e3` is much slower than on `e1`. This is because `e2` and `e3` have a class attribute. Even though there's no `$` method defined for `e2`, doing `e2$x` still about 6 times slower than `e1$x`, simply because R looks for an appropriate S3 method. -->

`e2`と`e3`で`$`を使うと，`e1`よりかなり低速です．これは`e2`と`e3`には`class`属性があるからです．`e2`には`$`メソッドは定義されていないにもかかわらず，Rが適切なS3メソッドを探すせいで，`e2$x`は`e1$x`の約6倍も低速です．

<!-- `e3$x` is slightly faster than `e2$x`; this is probably because the `$.e3` function doesn't actually do anything other than return NULL. -->

`e3$x`は`e2$x`よりわずかに高速です．これはおそらく，`$.e3`関数が実際には`NULL`を返す以外は何もしないからでしょう[^e3_perf]．

<!-- If an object has a class attribute, R will attempt to look for a method every time `$` is called. This can slow things down considerably, if `$` is used often. -->

オブジェクトが`class`属性を持つ場合，Rは`$`が呼び出されるたびにメソッドの探索を試みます．`$`が頻繁に使われる場合には，これによって大きく速度が低下することがあり得ます．

<!-- ## Lists vs. environments, and `$` vs. `[[` -->

## リストと環境，`$`と`[[`

<!-- Lists could also be used for creating classes (albeit not with reference semantics). How much time does it take to access items using `$` for lists vs. environments? We'll also compare using `obj$x` to `obj[['x']]`. -->

クラスを作成するのにはリストも使えます（参照セマンティクスではありませんが）．リストと環境において，`$`を用いて項目にアクセスするのにかかる時間はとれくらいでしょうか．また，`obj$x`を使う場合と`obj[['x']]`を使う場合も比較してみます．


```r
lst <- list(x = 10)
env <- new.env()
env$x <- 10

mb_summary(microbenchmark(
  lst = lst$x,
  env = env$x,
  lst[['x']],
  env[['x']]
))
#> Warning in microbenchmark(lst = lst$x, env = env$x, lst[["x"]],
#> env[["x"]]): Could not measure a positive execution time for 101
#> evaluations.
#>         name median
#> 1        lst      0
#> 2        env      0
#> 3 lst[["x"]]      0
#> 4 env[["x"]]      0
```

<!-- Performance is comparable across environments and lists. -->

環境とリストの性能は同等です．

<!-- The `[[` operator is slightly faster than `$`, probably because it doesn't need to convert the unevaluated symbol to a string. -->

`[[`演算子は`$`よりわずかに高速です．おそらく`[[`は未評価のシンボルを文字列に変換する必要がないからでしょう[^list_perf]．

*****

<!-- Wrap-up -->
<!-- ======= -->

まとめ
=======

<!-- R6 objects take less memory and are significantly faster than R's reference class objects, and they also have some options that provide for even more speed. -->

R6オブジェクトはRの参照クラスオブジェクトよりメモリ消費が少なく，はるかに高速です．また，R6にはさらに速度を向上させるオプションもあります．

<!-- In these tests, the biggest speedup for R6 classes comes from not using a class attribute; this speeds up the use of `$`. Non-portable R6 classes can also access fields without `$` at all, which provides another modest speed boost. In most cases, these speed increases are negligible -- they are on the order of microseconds and will be noticeable only when tens or even hundreds of thousands of class member accesses are performed. -->

以上のテストにおいて，R6クラスの速度を最も大きく向上させるのは，`class`属性を使わないことです．これは`$`を使うときの速度を向上させます．移植不可R6クラスも，フィールドに`$`をまったく使わずにアクセスできるので，やや速度を向上させることができます．ほとんどの場合において，これらの高速化は無視できる程度（マイクロ秒のオーダー）であり，クラスメンバへのアクセスが何万回あるいは何十万回も行われるときにのみ重要になるでしょう．


*****

<!-- Appendix -->
<!-- ======== -->

付録
========

<!-- ## Functions for calculating object sizes -->

## オブジェクトサイズを計算する関数


```r
# サイズ計算用の便利関数
obj_size <- function(expr, .env = parent.frame()) {
  size_n <- function(n = 1) {
    objs <- lapply(1:n, function(x) eval(expr, .env))
    as.numeric(do.call(object_size, objs))
  }

  data.frame(one = size_n(1), incremental = size_n(2) - size_n(1))
}

obj_sizes <- function(..., .env = parent.frame()) {
  exprs <- as.list(match.call(expand.dots = FALSE)$...)
  names(exprs) <- lapply(1:length(exprs),
    FUN = function(n) {
      name <- names(exprs)[n]
      if (is.null(name) || name == "") paste(deparse(exprs[[n]]), collapse = " ")
      else name
    })

  sizes <- mapply(obj_size, exprs, MoreArgs = list(.env = .env), SIMPLIFY = FALSE)
  do.call(rbind, sizes)
}
```
<!-- # Utility functions for calculating sizes -->


<!-- ## System information -->

## システム情報 [^system_info]


```r
sessionInfo()
#> R version 3.3.2 (2016-10-31)
#> Platform: x86_64-w64-mingw32/x64 (64-bit)
#> Running under: Windows 10 x64 (build 14393)
#> 
#> locale:
#> [1] LC_COLLATE=Japanese_Japan.932  LC_CTYPE=Japanese_Japan.932   
#> [3] LC_MONETARY=Japanese_Japan.932 LC_NUMERIC=C                  
#> [5] LC_TIME=Japanese_Japan.932    
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] scales_0.4.1           ggplot2_2.2.0.9000     R6_2.2.0              
#> [4] pryr_0.1.2             microbenchmark_1.4-2.1
#> 
#> loaded via a namespace (and not attached):
#>  [1] Rcpp_0.12.8      knitr_1.15.1     magrittr_1.5     MASS_7.3-45     
#>  [5] splines_3.3.2    munsell_0.4.3    lattice_0.20-34  colorspace_1.3-1
#>  [9] multcomp_1.4-6   stringr_1.1.0    plyr_1.8.4       tools_3.3.2     
#> [13] grid_3.3.2       gtable_0.2.0     TH.data_1.0-7    htmltools_0.3.5 
#> [17] survival_2.40-1  yaml_2.1.14      lazyeval_0.2.0   rprojroot_1.1   
#> [21] digest_0.6.10    assertthat_0.1   tibble_1.2       Matrix_1.2-7.1  
#> [25] codetools_0.2-15 evaluate_0.10    rmarkdown_1.2    labeling_0.3    
#> [29] sandwich_2.3-4   stringi_1.1.2    backports_1.0.4  mvtnorm_1.0-5   
#> [33] zoo_1.7-13
```

[^test_obj_size]: 訳注：以下，テスト結果は訳者の環境におけるものである．
[^e3_perf]: 訳注：訳者の環境では`e3$x`に`e2$x`の速度にほとんど差がなかった．また，翻訳時点の[原文](https://cran.r-project.org/package=R6/vignettes/Performance.html)でも，コードの実行結果上では`e2$x`の方が高速となっている．
[^list_perf]: 訳注：訳者の環境では実行時間が短すぎて中央値を計測するのが困難であった．[原文](https://cran.r-project.org/package=R6/vignettes/Performance.html)を参照されたい．
[^system_info]: 訳注：システム情報は訳者のものである．