11.4 時間のかかるチャンクをキャッシュする

コードチャンクの実行に時間がかかる場合, チャンクオプション cache = TRUE で結果をキャッシュすることを検討するとよいでしょう. キャッシュが有効な場合, このコードが以前にも実行され, その後コードに変更がないならば, knitr はこの実行を飛ばします. コードチャンクを変更し, つまりコードまたはチャンクオプションを修正したなら, 過去のキャッシュは自動的に無効になり knitr はもう一度チャンクをキャッシュします.

キャッシュされたコードチャンクに関しては, チャンクが再度実行されたかのように, 過去の実行結果から出力とオブジェクトが自動的に読み込まれます. キャッシュを読み込むほうが結果を計算するよりはるかに速いという場合に役に立ちます. しかしながら, うまい話というのは世に存在しません. あなたの使う場面によっては, キャッシュがどのように動作するかをより学びぶ必要があるでしょう. 特に cache invalidation を学べば, knitrがしょっちゅうキャッシュを無効化したり, あるいは時に無効化が十分しないことに振り回されることなく, キャッシュの利点を最大限活かすことができます.

最も適切なキャッシュの使用例は, コードチャンク内での計算に非常に時間がかかる R オブジェクトの保存と再読込に使うことですが, コードが options() を使って R のグローバルオプションを変更するといった副産物 (このような変更はキャッシュされません) があってはなりません. コードチャンクに副産物があるなら, キャッシュを使わないことをお薦めします.

最初のほうで簡単に書いたように, キャッシュの動きはチャンクオプションに依存します. もし include 以外のチャンクオプションを変更したら, キャッシュは無効化されます. この仕様はよくある問題を解決してくれます. それは外部データファイルを読み込むときに, ファイルが更新されていたならキャッシュを無効化したい, というような場合です. 単純に cache = TRUE を使うだけでは不十分です.

```{r import-data, cache=TRUE}
d <- read.csv('my-precious.csv')
```

knitr にデータファイルが変更されたかどうかを教えなければなりません. 1つの方法としては別のチャンクオプション cache.extra = file.mtime('my-precious.csv') を加えることですが, あるいはより厳密には cache.extra = tools::md5sum('my-precious.csv') を追加することがあります. 前者はファイルの更新時刻が変更されたらキャッシュを無効化する, という意味です. 後者はファイルの中身が変更されたらキャッシュを更新するということです. cache.extraknitr の組み込みのチャンクオプション名ではないということに注意してください. 他の組み込みのオプション名と競合しない限り, この用途のオプションには好きな名前を使うことができます.

同様に, 他の情報をキャッシュと関連付けることができます. 例えば R のバージョンなら cache.extra = getRversion(), システム日付なら cache.extra = Sys.Date(), オペレーティングシステムなら cache.extra = Sys.info()[['sysname']] というようにすると, これらの条件が変更されたときにキャッシュは正しく無効化されます.

文書全体で cache = TRUE を設定することはお薦めしません. キャッシュはかなり扱いにくいものです. 代わりに, 実行に時間がかかり副産物がないとはっきりしているコードチャンクに限って, 個別にキャッシュを有効化することをお薦めします.

knitr のキャッシュの設計に不満があるなら, 自分でオブジェクトのキャッシュを取ることもできます. 以下はごく簡単な例です.

if (file.exists("results.rds")) {
  res <- readRDS("results.rds")
} else {
  res <- compute_it()  # 実行時間のかかる関数
  saveRDS(res, "results.rds")
}

この例では, キャッシュを無効化する唯一の, そして簡単な方法は result.rds ファイルを削除することです. この簡単なキャッシュのしくみが気に入ったなら, 14.9節で紹介する xfun::cache_rds() を使うこともできます.