在R中快速重复构建软件包

3

我正在编写一个 R 包,其中包含大量复杂的 C++ 代码,编译需要一段时间。

当我更改代码时,我想快速重建包以便测试。然而,R CMD build 每次似乎都是从头开始编译,而不是使用我的代码的 makefiles 执行所需的内容。

有没有一种方法可以快速地在 R 中对一个包进行重复构建以进行测试?

2个回答

4
我有点拖欠一篇关于这个的简短博客文章,但我之前提到过几次:使用ccache。当文件不改变(例如仅更改帮助页面)或更改较少时,它可以极大地帮助。缓存是一个非常聪明的技巧,该软件包非常健壮。
在Ubuntu/Debian上:使用sudo apt-get install ccache,然后在~/.R/Makevars中添加以下内容:
VER=
CCACHE=ccache
#CCACHE=
CC=$(CCACHE) gcc$(VER)
CXX=$(CCACHE) g++$(VER)
CXX11=$(CCACHE) g++$(VER)
CXX14=$(CCACHE) g++$(VER)

这还允许在g++版本之间切换。将其更改为clang++留给读者自己练习;-)

此外,查看R CMD buildR CMD INSTALL的选项,以跳过vignette和/或手动构建以进一步加快重新构建速度。

示例:这是对Rcpp本身进行重新安装(从git pull中获取)的过程,在我(像样的)工作机器上,第一次安装需要21.9秒,然后由于ccache,第二次只需要1.4秒:

~/git/rcpp(master)$ time R CMD INSTALL .
* installing to library ‘/usr/local/lib/R/site-library’
* installing *source* package ‘Rcpp’ ...
** libs
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Date.cpp -o Date.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Module.cpp -o Module.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Rcpp_init.cpp -o Rcpp_init.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c api.cpp -o api.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c attributes.cpp -o attributes.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c barrier.cpp -o barrier.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o Rcpp.so Date.o Module.o Rcpp_init.o api.o attributes.o barrier.o -L/usr/lib/R/lib -lR
installing to /usr/local/lib/R/site-library/Rcpp/libs
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (Rcpp)

real    0m21.917s
user    0m21.388s
sys     0m2.304s
~/git/rcpp(master)$ ./cleanup 
~/git/rcpp(master)$ time R CMD INSTALL .
* installing to library ‘/usr/local/lib/R/site-library’
* installing *source* package ‘Rcpp’ ...
** libs
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Date.cpp -o Date.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Module.cpp -o Module.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c Rcpp_init.cpp -o Rcpp_init.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c api.cpp -o api.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c attributes.cpp -o attributes.o
ccache g++ -I/usr/share/R/include -DNDEBUG -I../inst/include/     -fpic  -g -O3 -Wall -pipe -pedantic -Wextra -Wno-empty-body -Wno-unused -c barrier.cpp -o barrier.o
g++ -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o Rcpp.so Date.o Module.o Rcpp_init.o api.o attributes.o barrier.o -L/usr/lib/R/lib -lR
installing to /usr/local/lib/R/site-library/Rcpp/libs
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** installing vignettes
** testing if installed package can be loaded
* DONE (Rcpp)

real    0m1.444s
user    0m1.380s
sys     0m1.452s
~/git/rcpp(master)$ 

几年后的编辑: 我写了那篇博客文章, 它包含另一个重要的提示。一个人确实需要一个文件~/.ccache/ccache.conf,因为a)R会新鲜地解压文件(所以“较新的ctime”),并将它们放入临时目录中。因此,在使用R CMD INSTALL somepkg_1.2.3.tar.gz(而不仅仅是源代码)时,这很有帮助:

max_size = 5.0G
# important for R CMD INSTALL *.tar.gz as tarballs are expanded freshly -> fresh ctime
sloppiness = include_file_ctime
# also important as the (temp.) directory name will differ
hash_dir = false

我坚持我所写的,并展示了:从21秒降至1.5秒。我在多台机器上使用ccache已经有十年左右了。 - Dirk Eddelbuettel
请不要未经允许修改我的帖子。我将在几分钟内撤销您的编辑。 - Dirk Eddelbuettel
抱歉。在没有编辑的情况下,从我的角度来看,答案是有用但不完整的。因此,尽管我已经点赞了它,但我不能接受它。 - Richard

1

Dirk的回答指引我朝正确的方向,但仍然不够。由于他要求我不要附加我所需的最后几个步骤,因此我在这里附上。 如果没有以下内容,我的问题将无法得到解答。

对我而言,Dirk的回答并不能立即解决问题。如果编译仍然感到缓慢,请尝试运行:

ccache -s

结果会看起来像这样。
cache directory                     /home/myuser/.ccache
primary config                      /home/myuser/.ccache/ccache.conf
secondary config      (readonly)    /etc/ccache.conf
cache hit (direct)                     0
cache hit (preprocessed)               0
cache miss                           989
cache hit rate                         0 %
called for link                       12
preprocessor error                    12
cleanups performed                    16
files in cache                       177
cache size                          31.1 MB
max cache size                       5.0 GB
Note that cache isn't getting hit, which means ccache isn't doing anything.

您可以使用:

export CCACHE_LOGFILE=ccache.log
R CMD build .

我需要进行一些调试,但对我没有帮助。

解决问题的方法是运行:

export CCACHE_NOHASHDIR=true
R CMD build .

事实证明,ccache有时会考虑文件的位置。R CMD build .似乎在一个临时目录中构建,因此文件的位置每次都在改变。

这个答案是错误/不适用的,因为它是由于缺乏对正确构建过程的理解所导致的。正如R文档明确说明的那样,标准构建过程首先是创建一个.tar.gz文件,然后(在可选检查之后)通过R CMD INSTALL pkg_1.2-3.tar.gz从tarball安装。或者,在开发过程中,从目录中运行R CMD INSTALL .进行安装。R CMD build是完全错误的。 - Dirk Eddelbuettel
以上评论关注了答案中不重要的一个方面,并未提到 R CMD build 生成了相关的 tarball,从而提供了有关编译能力的即时反馈,避免了不必要的安装。 - Richard
Dirk:它描述了我所需要的。我希望它能像你说的那样正常工作,但事实并非如此。看起来我们的开发环境不同。 - Richard
1
cd ..; R CMD build mydir/; R CMD check mydir_1.2-3.tar.gz; R CMD INSTALL mydir_1.2-3.tar.gz记录在案推荐的工作流程,对我来说始终使用ccacheR CMD INSTALL .也是如此。 - Dirk Eddelbuettel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接