問題提議
tikzDeviceはRのグラフをTeXのグラフィックフレームワークであるTikZ形式を出力するパッケージです。
基本的には普通の画像形式(pdf,pngなど)のデバイスを指定するかわりにtikz()
を使用するだけで.tex
形式で書き出しができますが、書き出しが非常に遅いという問題点があります。
以下はpdf,png,tikzで10000個の点をプロットした結果です(@MacBook Air Early 2015)。
$ time Rscript -e "library(tikzDevice); pdf('out'); plot(1:10000); dev.off();"
real 0m0.529s
$ time Rscript -e "library(tikzDevice); png('out'); plot(1:10000); dev.off();"
real 0m0.604s
$ time Rscript -e "library(tikzDevice); tikz('out'); plot(1:10000); dev.off();"
real 0m3.884s
このように、TikZにするだけで約7倍も時間がかかってしまいます。
原因
書き出しが遅くなる理由は、tikz
関数内でTeXで使用される文字や文字列の幅を実行時にpdflatex
を使用してチェックするためです。
対話形式で実行時に表示されるログを見ると以下のようなファイルが処理されることが確認できます。
> plot(1:10000)
Measuring dimensions of: m
Running command: '/path/to/pdflatex' ... '/var/.../tikzDevice15ad45b476a03/tikzStringWidthCalc.tex'
Measuring dimensions of: 0
Running command: '/path/to/pdflatex' ... '/var/.../tikzDevice15ad42d8119c6/tikzStringWidthCalc.tex'
Measuring dimensions of: \char77
Running command: '/path/to/pdflatex' ... '/var/.../tikzDevice15ad476c32d80/tikzStringWidthCalc.tex'
Measuring dimensions of: 2000
Running command: '/path/to/pdflatex' ... '/var/.../tikzDevice15ad47e06b6a/tikzStringWidthCalc.tex'
...
対策1
tikzDevice
ではこの計測結果をファイルにキャッシュするためのtikzMetricsDictionary
オプションを提供しています(正確には未指定時にはランダムなパスでキャッシュファイルが作成され、Rの終了時に削除されます)。
$ time Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('out'); plot(1:10000); dev.off()"
real 0m3.815s
$ time Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('out'); plot(1:10000); dev.off()"
real 0m0.512s
これを常に静的なパスに設定すれば2回目以降の出力は高速化されますが、複数のRプロセスを同時に動かす場合に競合してエラーが発生する場合があります。
$ Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('1'); plot(1:10000); dev.off()" &
$ Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('2'); plot(1:10000); dev.off()" &
$ Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('3'); plot(1:10000); dev.off()" &
$ Rscript -e "library(tikzDevice); options(tikzMetricsDictionary='/tmp/dict'); tikz('4'); plot(1:10000); dev.off()" &
$ wait
createLockFile(lockname) でエラー:
cannot create lock file ‘/private/tmp/dict___LOCK’
呼び出し: plot ... evalWithoutInterrupts -> dbInsert -> dbInsert -> createLockFile
実行が停止されました
tikzDevice
の使い方としては論文中の複数のグラフをRで生成することが想定されるため、このままでは並列コンパイル(make -j
)が正常に動かなくなります。
対策2
上記のエラーは、Rscript実行時のコマンドライン引数等を使ってハッシュをとることで簡易的に対策することができます。
library(openssl) # md5を使用
pwd.and.args = paste(c(getwd(), commandArgs(trailingOnly=F)), collapse="")
pwd.and.args.hash = md5(pwd.and.args)
options(tikzMetricsDictionary=paste0("/tmp/tikz-", pwd.and.args.hash))
以上を.Rprofile
などに書いておくと/tmp/tikz-[適当なハッシュ値]
にキャッシュされます。