├── .gitignore ├── tests ├── testthat.R └── testthat │ └── test-pkg_examples.R ├── inst ├── examples │ ├── RcppProgressETA │ │ ├── NAMESPACE │ │ ├── src │ │ │ ├── Makevars │ │ │ ├── example.cpp │ │ │ └── eta_progress_bar.hpp │ │ ├── R │ │ │ └── examples.R │ │ └── DESCRIPTION │ ├── RcppProgressExample │ │ ├── NAMESPACE │ │ ├── src │ │ │ ├── Makevars │ │ │ ├── dummy.cpp │ │ │ └── example.cpp │ │ ├── R │ │ │ └── examples.R │ │ ├── DESCRIPTION │ │ └── man │ │ │ ├── test_multithreaded.Rd │ │ │ ├── RcppProgressExample-package.Rd │ │ │ └── test_sequential.Rd │ └── RcppProgressArmadillo │ │ ├── NAMESPACE │ │ ├── src │ │ ├── Makevars │ │ └── example.cpp │ │ ├── R │ │ └── examples.R │ │ ├── DESCRIPTION │ │ └── man │ │ ├── test_multithreaded.Rd │ │ ├── RcppProgressExample-package.Rd │ │ └── test_sequential.Rd ├── include │ ├── progress_bar.hpp │ ├── interrupts.hpp │ ├── simple_progress_bar.hpp │ ├── progress.hpp │ └── interruptable_progress_monitor.hpp └── Makefile ├── NAMESPACE ├── .Rbuildignore ├── .travis.yml ├── DESCRIPTION ├── docker_checker └── Dockerfile ├── R └── wrap_examples.R ├── NEWS.md ├── README.md ├── Makefile └── man └── RcppProgress-package.Rd /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | *.tar.gz 3 | *.o 4 | *.so 5 | lib 6 | *.pdf 7 | rhub/ 8 | 9 | -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(RcppProgress) 3 | 4 | test_check("RcppProgress") 5 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/NAMESPACE: -------------------------------------------------------------------------------- 1 | useDynLib(RcppProgressETA) 2 | exportPattern("^[[:alpha:]]+") 3 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | importFrom(Rcpp, evalCpp) 2 | importFrom(devtools, load_all) 3 | #exportPattern("^[[:alpha:]]+") 4 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/NAMESPACE: -------------------------------------------------------------------------------- 1 | useDynLib(RcppProgressExample) 2 | exportPattern("^[[:alpha:]]+") 3 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/NAMESPACE: -------------------------------------------------------------------------------- 1 | useDynLib(RcppProgressArmadillo) 2 | exportPattern("^[[:alpha:]]+") 3 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | NEWS.md 2 | Makefile 3 | ^test_rcpp.*\.R$ 4 | \.tar.gz$ 5 | docker_checker 6 | code 7 | lib 8 | ^\..+ 9 | \.o 10 | \.so 11 | rhub 12 | manual.pdf 13 | 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: r 2 | cache: packages 3 | 4 | pandoc: false 5 | 6 | #before_script: 7 | # - make tests 8 | 9 | r_github_packages: 10 | - jimhester/covr 11 | 12 | after_success: 13 | - Rscript -e 'covr::codecov()' 14 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/src/Makevars: -------------------------------------------------------------------------------- 1 | ## Use the R_HOME indirection to support installations of multiple R version 2 | PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(SHLIB_OPENMP_CXXFLAGS) 3 | PKG_CXXFLAGS += -Wall $(SHLIB_OPENMP_CXXFLAGS) 4 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/src/Makevars: -------------------------------------------------------------------------------- 1 | ## Use the R_HOME indirection to support installations of multiple R version 2 | PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(SHLIB_OPENMP_CXXFLAGS) 3 | PKG_CXXFLAGS += -Wall $(SHLIB_OPENMP_CXXFLAGS) 4 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/src/Makevars: -------------------------------------------------------------------------------- 1 | ## Use the R_HOME indirection to support installations of multiple R version 2 | PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(SHLIB_OPENMP_CXXFLAGS) 3 | PKG_CXXFLAGS += -Wall $(SHLIB_OPENMP_CXXFLAGS) 4 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/src/dummy.cpp: -------------------------------------------------------------------------------- 1 | /* regression test 2 | dummy source to test impact of multiple inclusions 3 | of RcppProgress header files. 4 | */ 5 | 6 | 7 | #include "progress.hpp" 8 | 9 | void toto() { 10 | Progress p(100, false); 11 | } 12 | 13 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/R/examples.R: -------------------------------------------------------------------------------- 1 | 2 | test_sequential <- function(max=100, nb=1000, display_progress=TRUE) { 3 | .Call("test_sequential_wrapper2", max, nb, display_progress, PACKAGE = "RcppProgressETA") 4 | invisible() 5 | } 6 | 7 | test_multithreaded <- function(max=100, nb=1000, threads=0, display_progress=TRUE) { 8 | .Call("test_multithreaded_wrapper2", max, nb, threads, display_progress, PACKAGE = "RcppProgressETA") 9 | invisible() 10 | } 11 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/R/examples.R: -------------------------------------------------------------------------------- 1 | 2 | test_sequential <- function(max=100, nb=1000, display_progress=TRUE) { 3 | .Call("test_sequential_wrapper2", max, nb, display_progress, PACKAGE = "RcppProgressExample") 4 | invisible() 5 | } 6 | 7 | test_multithreaded <- function(max=100, nb=1000, threads=0, display_progress=TRUE) { 8 | .Call("test_multithreaded_wrapper2", max, nb, threads, display_progress, PACKAGE = "RcppProgressExample") 9 | invisible() 10 | } -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/R/examples.R: -------------------------------------------------------------------------------- 1 | 2 | test_sequential <- function(max=100, nb=1000, display_progress=TRUE) { 3 | .Call("test_sequential_wrapper2", max, nb, display_progress, PACKAGE = "RcppProgressArmadillo") 4 | invisible() 5 | } 6 | 7 | test_multithreaded <- function(max=100, nb=1000, threads=0, display_progress=TRUE) { 8 | .Call("test_multithreaded_wrapper2", max, nb, threads, display_progress, PACKAGE = "RcppProgressArmadillo") 9 | invisible() 10 | } -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppProgressExample 2 | Type: Package 3 | Title: Example use of the RcppProgress package 4 | Version: 0.2 5 | Date: 2012-04-17 6 | Author: Karl Forner 7 | Maintainer: Karl Forner 8 | Description: Example use of the RcppProgress package in a package 9 | License: GPL (>=3) 10 | Depends: Rcpp (>= 0.9.10), RcppProgress (>= 0.1) 11 | LinkingTo: Rcpp, RcppProgress 12 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppProgressETA 2 | Type: Package 3 | Title: Example use of the RcppProgress package with an ETAProgressBar 4 | Version: 0.1 5 | Date: 2017-11-10 6 | Author: Karl Forner 7 | Maintainer: Karl Forner 8 | Description: Example use of the RcppProgress package with a custom progress bar 9 | License: GPL (>=3) 10 | Depends: Rcpp (>= 0.9.10), RcppProgress (>= 0.1) 11 | LinkingTo: Rcpp, RcppProgress 12 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppProgressArmadillo 2 | Type: Package 3 | Title: Example use of the RcppProgress package to reproduce a bug with RcppArmadillo 4 | Version: 0.2 5 | Date: 2014-11-25 6 | Author: Karl Forner 7 | Maintainer: Karl Forner 8 | Description: Example use of the RcppProgress package in a package, 9 | cf http://lists.r-forge.r-project.org/pipermail/rcpp-devel/2013-August/006288.html 10 | License: GPL (>=3) 11 | Depends: Rcpp (>= 0.9.10), RcppProgress (>= 0.1) 12 | LinkingTo: Rcpp, RcppProgress, RcppArmadillo 13 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: RcppProgress 2 | Maintainer: Karl Forner 3 | License: GPL (>= 3) 4 | Title: An Interruptible Progress Bar with OpenMP Support for C++ in R Packages 5 | Type: Package 6 | LazyLoad: yes 7 | Author: Karl Forner 8 | Description: Allows to display a progress bar in the R 9 | console for long running computations taking place in c++ code, 10 | and support for interrupting those computations even in multithreaded 11 | code, typically using OpenMP. 12 | URL: https://github.com/kforner/rcpp_progress 13 | BugReports: https://github.com/kforner/rcpp_progress/issues 14 | Version: 0.4 15 | Date: 2017-11-14 16 | Imports: 17 | Rcpp (>= 0.9.4), 18 | devtools 19 | Suggests: 20 | RcppArmadillo, 21 | roxygen2, 22 | testthat 23 | RoxygenNote: 6.0.1 24 | -------------------------------------------------------------------------------- /inst/include/progress_bar.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * progress_bar.hpp 3 | * 4 | * An abstract class for classes that display a progress bar 5 | * 6 | * Author: karl.forner@gmail.com 7 | * 8 | */ 9 | #ifndef _RcppProgress_PROGRESS_BAR_HPP 10 | #define _RcppProgress_PROGRESS_BAR_HPP 11 | 12 | class ProgressBar { 13 | public: 14 | 15 | // subclasses should not rely on the destructor to finalize the display 16 | virtual ~ProgressBar() = 0; 17 | 18 | // start the display. It will be updated by subsequent calls to update() 19 | virtual void display() = 0; 20 | 21 | // update if needed the display 22 | virtual void update(float progress) = 0; 23 | 24 | // finalize the display 25 | virtual void end_display() = 0; 26 | }; 27 | 28 | inline ProgressBar::~ProgressBar() {} 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/man/test_multithreaded.Rd: -------------------------------------------------------------------------------- 1 | \name{test_sequential} 2 | \alias{test_sequential} 3 | \title{ 4 | Test/example of using RcppProgress in sequential code, i.e not in multithreaded code 5 | } 6 | \description{ 7 | Test function for the package rngOpenMP in a sequential mode. 8 | } 9 | \usage{ 10 | test_sequential(max=100, nb=1000, display_progress=TRUE) 11 | } 12 | \arguments{ 13 | \item{max}{ number of long computation jobs to perform } 14 | \item{nb}{ number of interruptible steps a long computaton job is made of } 15 | \item{display_progress}{ whether to display the progress bar or not } 16 | } 17 | \details{ 18 | Will perform max long jobs made of nb interruptible steps. 19 | The jobs are performed sequentially. The computation can be interrupted by the user. 20 | } 21 | \value{ 22 | None 23 | } 24 | \author{ 25 | Karl Forner 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/man/test_multithreaded.Rd: -------------------------------------------------------------------------------- 1 | \name{test_sequential} 2 | \alias{test_sequential} 3 | \title{ 4 | Test/example of using RcppProgress in sequential code, i.e not in multithreaded code 5 | } 6 | \description{ 7 | Test function for the package rngOpenMP in a sequential mode. 8 | } 9 | \usage{ 10 | test_sequential(max=100, nb=1000, display_progress=TRUE) 11 | } 12 | \arguments{ 13 | \item{max}{ number of long computation jobs to perform } 14 | \item{nb}{ number of interruptible steps a long computaton job is made of } 15 | \item{display_progress}{ whether to display the progress bar or not } 16 | } 17 | \details{ 18 | Will perform max long jobs made of nb interruptible steps. 19 | The jobs are performed sequentially. The computation can be interrupted by the user. 20 | } 21 | \value{ 22 | None 23 | } 24 | \author{ 25 | Karl Forner 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/man/RcppProgressExample-package.Rd: -------------------------------------------------------------------------------- 1 | \name{RcppProgressExample-package} 2 | \alias{RcppProgressExample-package} 3 | \alias{RcppProgressExample} 4 | \docType{package} 5 | \title{ 6 | This package is an example of how to use RcppProgress in your own package. 7 | } 8 | \description{ 9 | This package is an example of how to use RcppProgress in your own package. 10 | } 11 | \details{ 12 | \tabular{ll}{ 13 | Package: \tab RcppProgressExample\cr 14 | Type: \tab Package\cr 15 | Version: \tab 0.1\cr 16 | Date: \tab 2012-04-17\cr 17 | License: \tab GPL\cr 18 | } 19 | ~~ An overview of how to use the package, including the most important ~~ 20 | ~~ functions ~~ 21 | } 22 | \author{ 23 | Karl FORNER 24 | 25 | Maintainer: Karl Forner 26 | } 27 | 28 | 29 | \keyword{ package } 30 | \seealso{ 31 | \code{\link[RcppProgress:RcppProgress-package]{RcppProgress}} 32 | } 33 | 34 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/man/RcppProgressExample-package.Rd: -------------------------------------------------------------------------------- 1 | \name{RcppProgressExample-package} 2 | \alias{RcppProgressExample-package} 3 | \alias{RcppProgressExample} 4 | \docType{package} 5 | \title{ 6 | This package is an example of how to use RcppProgress in your own package. 7 | } 8 | \description{ 9 | This package is an example of how to use RcppProgress in your own package. 10 | } 11 | \details{ 12 | \tabular{ll}{ 13 | Package: \tab RcppProgressExample\cr 14 | Type: \tab Package\cr 15 | Version: \tab 0.1\cr 16 | Date: \tab 2012-04-17\cr 17 | License: \tab GPL\cr 18 | } 19 | ~~ An overview of how to use the package, including the most important ~~ 20 | ~~ functions ~~ 21 | } 22 | \author{ 23 | Karl FORNER 24 | 25 | Maintainer: Karl Forner 26 | } 27 | 28 | 29 | \keyword{ package } 30 | \seealso{ 31 | \code{\link[RcppProgress:RcppProgress-package]{RcppProgress}} 32 | } 33 | 34 | -------------------------------------------------------------------------------- /tests/testthat/test-pkg_examples.R: -------------------------------------------------------------------------------- 1 | context('RcppProgressExample sequential\n') 2 | 3 | .test_sequential <- function() { 4 | expect_error(RcppProgress:::test_sequential(nb = 500), NA) 5 | } 6 | test_that("test_sequential", .test_sequential()) 7 | 8 | 9 | context('RcppProgressExample multithreaded\n') 10 | .test_multithreaded <- function() { 11 | RcppProgress:::test_multithreaded(nb = 1000, threads = 4) 12 | } 13 | test_that("test_multithreaded", .test_multithreaded()) 14 | 15 | 16 | context('RcppProgressArmadillo multithreaded\n') 17 | .amardillo_multithreaded <- function() { 18 | RcppProgress:::amardillo_multithreaded(nb = 1000, threads = 4) 19 | } 20 | test_that("amardillo_multithreaded", .amardillo_multithreaded()) 21 | 22 | 23 | 24 | context('RcppProgressETA:custom progress bar\n') 25 | .eta_progress_bar <- function() { 26 | RcppProgress:::eta_progress_bar(nb = 1000) 27 | } 28 | test_that("eta_progress_bar", .eta_progress_bar()) -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/man/test_sequential.Rd: -------------------------------------------------------------------------------- 1 | \name{test_multithreaded} 2 | \alias{test_multithreaded} 3 | \title{ 4 | Test/example of using RcppProgress in multithreaded code 5 | } 6 | \description{ 7 | Test function for the package rngOpenMP in a multithreaded mode. 8 | } 9 | \usage{ 10 | test_multithreaded(max=100, nb=1000, threads=0, display_progress=TRUE) 11 | } 12 | \arguments{ 13 | \item{max}{ number of long computation jobs to perform } 14 | \item{nb}{ number of interruptible steps a long computaton job is made of } 15 | \item{threads}{ number of threads to use, or 0 to use the default OpenMP number, that is usually the number of cores } 16 | \item{display_progress}{ whether to display the progress bar or not } 17 | } 18 | \details{ 19 | Will perform max long jobs made of nb interruptible steps. 20 | The jobs are parallelized. The computation can be interrupted by the user. 21 | } 22 | \value{ 23 | None 24 | } 25 | \author{ 26 | Karl Forner 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/man/test_sequential.Rd: -------------------------------------------------------------------------------- 1 | \name{test_multithreaded} 2 | \alias{test_multithreaded} 3 | \title{ 4 | Test/example of using RcppProgress in multithreaded code 5 | } 6 | \description{ 7 | Test function for the package rngOpenMP in a multithreaded mode. 8 | } 9 | \usage{ 10 | test_multithreaded(max=100, nb=1000, threads=0, display_progress=TRUE) 11 | } 12 | \arguments{ 13 | \item{max}{ number of long computation jobs to perform } 14 | \item{nb}{ number of interruptible steps a long computaton job is made of } 15 | \item{threads}{ number of threads to use, or 0 to use the default OpenMP number, that is usually the number of cores } 16 | \item{display_progress}{ whether to display the progress bar or not } 17 | } 18 | \details{ 19 | Will perform max long jobs made of nb interruptible steps. 20 | The jobs are parallelized. The computation can be interrupted by the user. 21 | } 22 | \value{ 23 | None 24 | } 25 | \author{ 26 | Karl Forner 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /docker_checker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM rocker/r-devel 2 | MAINTAINER karl.forner@quartzbio.com 3 | 4 | # fix a problem in rocker/r-devel ? 5 | RUN rm -rf /var/lib/apt/lists/* 6 | 7 | 8 | RUN apt-get update && apt-get install -y \ 9 | libcurl4-openssl-dev libssl-dev libxml2-dev sudo nano pandoc 10 | 11 | ## make Rdevel the default R 12 | RUN cd /usr/local/bin/ && mv Rdevel R && mv Rscriptdevel Rscript 13 | RUN cd /usr/bin/ && mv R Rbase && mv Rscript Rscriptbase 14 | RUN echo "R_LIBS=\${R_LIBS-'/usr/local/lib/R/site-library:/usr/local/lib/R/library'}" >> /usr/local/lib/R/etc/Renviron 15 | 16 | # there are R-base packages already installed, remove them 17 | RUN rm -rf /usr/local/lib/R/site-library/* 18 | # do not use install2.r, which uses r, which uses R base 19 | RUN Rscript -e 'install.packages(c("covr", "devtools", "RcppArmadillo" , "roxygen2", "testthat"), Ncpus = 8)' 20 | #RUN install2.r BH bit covr devtools DNAtools RcppArmadillo roxygen2 testthat 21 | 22 | # install rhub, for testing package on rhub builder 23 | RUN Rscript -e 'devtools::install_github("r-hub/rhub")' 24 | 25 | -------------------------------------------------------------------------------- /inst/Makefile: -------------------------------------------------------------------------------- 1 | 2 | PKG=${shell pwd} 3 | PKGNAME=${shell basename ${PKG}} 4 | R=R 5 | RSCRIPT=Rscript 6 | 7 | R_SOURCES := $(wildcard R/*.[R|r]) 8 | TEST_SOURCES := $(wildcard tests/*.[R|r]) 9 | NAMESPACE = NAMESPACE 10 | DESCRIPTION = DESCRIPTION 11 | 12 | .PHONY: example 13 | 14 | all: ${NAMESPACE} 15 | 16 | # generates the manual in pdf: Rd2.pdf 17 | 18 | pdf: Rd2.pdf 19 | 20 | pdfclean: 21 | rm -rf .Rd2dvi* 22 | 23 | Rd2.pdf: ${NAMESPACE} ${DESCRIPTION} ${R_SOURCES} 24 | rm -f Rd2.pdf 25 | $R CMD Rd2pdf man/ 26 | 27 | 28 | 29 | example: 30 | mkdir -p .install 31 | R CMD build . 32 | R CMD INSTALL -l .install RcppProgress_0.2.tar.gz 33 | R CMD build inst/examples/RcppProgressExample 34 | R CMD INSTALL -l .install RcppProgressExample_0.2.tar.gz 35 | 36 | # run R CMD check 37 | check: ${NAMESPACE} ${DESCRIPTION} clean 38 | $R CMD build . 39 | $R CMD check --as-cran RcppProgress_0.2.tar.gz 40 | 41 | clean: pdfclean 42 | rm -f src/*.o src/*.so src/libsrc/*.o */*~ *~ 43 | rm -f config.log config.status *.tar.gz Rd2.pdf 44 | rm -rf .install RcppProgress.Rcheck 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /inst/include/interrupts.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Check for user interruption in C++ code interrupting execution of the current code 3 | * 4 | * This code has been written by Simon Urbanek 5 | * I took it from the R-devel mailing list 6 | * in the thread "[Rd] Interrupting C++ code execution" 7 | * The mail is dated: 25 april 2011 8 | * 9 | * It allows to check for user interruption without 10 | * leaving the c++ function that calls it. 11 | * 12 | * Potential drawbacks according to its author: 13 | * The problem with it is that it will eat all errors, even if they were not yours 14 | * (e.g. those resulting from events triggered the event loop), so I would not recommend it for general use. 15 | * 16 | */ 17 | #ifndef _RcppProgress_INTERRUPTS_HPP 18 | #define _RcppProgress_INTERRUPTS_HPP 19 | 20 | // N.B: seems the right way to include Rinternals.h in a Rcpp package 21 | #include 22 | 23 | 24 | static void chkIntFn(void *dummy) { 25 | R_CheckUserInterrupt(); 26 | } 27 | 28 | // this will call the above in a top-level context so it won't longjmp-out of your context 29 | inline bool checkInterrupt() { 30 | return (R_ToplevelExec(chkIntFn, NULL) == FALSE); 31 | } 32 | 33 | // fix a bug because of the length macro (in sglOptim_1.0.122.1) 34 | #undef length 35 | #endif 36 | -------------------------------------------------------------------------------- /R/wrap_examples.R: -------------------------------------------------------------------------------- 1 | load_my_example_pkg <- function(pkg, recompile = TRUE, ...) { 2 | path <- system.file(file.path('examples', pkg), package = 'RcppProgress') 3 | load_all(path, quiet = TRUE, recompile = recompile, ...) 4 | } 5 | 6 | get_function_from_pkg <- function(pkg, fun) { 7 | get(fun, getNamespace(pkg)) 8 | } 9 | 10 | test_sequential <- function(max = 100, nb = 1000, display_progress= TRUE, ...) { 11 | pkg <- 'RcppProgressExample' 12 | load_my_example_pkg(pkg, ...) 13 | fun <- get_function_from_pkg(pkg, 'test_sequential') 14 | fun(max, nb, display_progress) 15 | } 16 | 17 | # R wrapper for the example function #2 18 | test_multithreaded <- function(max = 100, nb = 1000, threads = 0, 19 | display_progress = TRUE, ...) 20 | { 21 | pkg <- 'RcppProgressExample' 22 | load_my_example_pkg(pkg, ...) 23 | fun <- get_function_from_pkg(pkg, 'test_multithreaded') 24 | fun(max, nb, threads, display_progress) 25 | } 26 | 27 | 28 | amardillo_multithreaded <- function(max = 100, nb = 1000, threads = 0, 29 | display_progress = TRUE, ...) 30 | { 31 | testthat::skip_if_not_installed('RcppArmadillo') 32 | pkg <- 'RcppProgressArmadillo' 33 | load_my_example_pkg(pkg, ...) 34 | fun <- get_function_from_pkg(pkg, 'test_multithreaded') 35 | fun(max, nb, threads, display_progress) 36 | } 37 | 38 | eta_progress_bar <- function(max = 100, nb = 1000, display_progress = TRUE) 39 | { 40 | pkg <- 'RcppProgressETA' 41 | load_my_example_pkg(pkg) 42 | fun <- get_function_from_pkg(pkg, 'test_sequential') 43 | fun(max, nb, display_progress) 44 | } 45 | 46 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # RcppProgress 0.4 2 | 3 | - RccpProgress now can use custom Progress Bars (draft by Clemens Schmid @nevrome) 4 | The include example package RcppProgressETA, included in RcppProgress is an 5 | example of using such a custom progress bar, and also contains an implementation 6 | (by Clemens Schmid @nevrome) of a vertical progress bar that displays the ETA 7 | (Estimated Time of completion). 8 | 9 | 10 | - reorganized the example tests functions: they are no longer implemented inside 11 | RcppProgress code, but called from the embedded RcppProgressExample example 12 | package. As a result RcppProgress no longer provides a dynamic library, nor 13 | does need to link against Rcpp. The example tests are also now available as 14 | testthat tests, thus used during the package check. 15 | 16 | - cleaned the Dockerfile and improved the docker-related Makefile targets 17 | 18 | - refactored the Makefile 19 | 20 | 21 | 22 | # RcppProgress 0.3 23 | 24 | * fixed issue #3: The Rcpp namespace is no longer open 25 | 26 | * fixed issue #2 about extra progress bar symbols being printed 27 | 28 | * refactored code by putting display code in class ProgressBar 29 | 30 | * fixed armadillo example warning. 31 | 32 | 33 | # RcppProgress 0.2 34 | 35 | * improvement by Jacques-Henri Jourdan to make it possible to use the progress monitor in multiple cpp files. 36 | 37 | * fixed compatibility problems with RcppArmadillo header. 38 | 39 | * provided a new example of using RcppArmadillo 40 | 41 | * used Rf_error instead of error in the implementation 42 | 43 | * add a note in the doc about RcppArmadillo 44 | 45 | * added this file 46 | 47 | 48 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressExample/src/example.cpp: -------------------------------------------------------------------------------- 1 | #include "progress.hpp" 2 | 3 | #include 4 | 5 | #include 6 | using namespace Rcpp; 7 | // your function for which to provide support 8 | void your_long_computation(int nb) { 9 | double sum = 0; 10 | for (int i = 0; i < nb; ++i) { 11 | if ( Progress::check_abort() ) 12 | return; 13 | for (int j = 0; j < nb; ++j) { 14 | sum += Rf_dlnorm(i+j, 0.0, 1.0, 0); 15 | } 16 | } 17 | } 18 | 19 | void test_sequential2(int max, int nb, bool display_progress) { 20 | 21 | Progress p(max, display_progress); 22 | for (int i = 0; i < max; ++i) { 23 | if ( p.increment() ) { 24 | your_long_computation(nb); 25 | } 26 | } 27 | } 28 | 29 | void test_multithreaded_omp2(int max, int nb, int threads, bool display_progress) { 30 | 31 | #ifdef _OPENMP 32 | if ( threads > 0 ) 33 | omp_set_num_threads( threads ); 34 | REprintf("Number of threads=%i\n", omp_get_max_threads()); 35 | #endif 36 | 37 | Progress p(max, display_progress); // create the progress monitor 38 | #ifdef _OPENMP 39 | #pragma omp parallel for schedule(dynamic) 40 | #endif 41 | for (int i = 0; i < max; ++i) { 42 | if ( p.increment() ) { // the only way to exit an OpenMP loop 43 | your_long_computation(nb); 44 | } 45 | } 46 | } 47 | 48 | RcppExport SEXP test_sequential_wrapper2(SEXP __max, SEXP __nb, SEXP __display_progress) { 49 | test_sequential2(as(__max), as(__nb), as(__display_progress)); 50 | return R_NilValue; 51 | } 52 | 53 | RcppExport SEXP test_multithreaded_wrapper2(SEXP __max, SEXP __nb, SEXP __threads, SEXP __display_progress) { 54 | test_multithreaded_omp2(as(__max), as(__nb), as(__threads), as(__display_progress)); 55 | return R_NilValue; 56 | } 57 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/src/example.cpp: -------------------------------------------------------------------------------- 1 | #include "progress.hpp" 2 | #include "eta_progress_bar.hpp" 3 | 4 | #include 5 | 6 | #include 7 | using namespace Rcpp; 8 | // your function for which to provide support 9 | void your_long_computation(int nb) { 10 | double sum = 0; 11 | for (int i = 0; i < nb; ++i) { 12 | if ( Progress::check_abort() ) 13 | return; 14 | for (int j = 0; j < nb; ++j) { 15 | sum += Rf_dlnorm(i+j, 0.0, 1.0, 0); 16 | } 17 | } 18 | } 19 | 20 | void test_sequential2(int max, int nb, bool display_progress) { 21 | ETAProgressBar pb; 22 | Progress p(max, display_progress, pb); 23 | for (int i = 0; i < max; ++i) { 24 | if ( p.increment() ) { 25 | your_long_computation(nb); 26 | } 27 | } 28 | } 29 | 30 | void test_multithreaded_omp2(int max, int nb, int threads, bool display_progress) { 31 | 32 | #ifdef _OPENMP 33 | if ( threads > 0 ) 34 | omp_set_num_threads( threads ); 35 | REprintf("Number of threads=%i\n", omp_get_max_threads()); 36 | #endif 37 | ETAProgressBar pb; 38 | Progress p(max, display_progress, pb); // create the progress monitor 39 | #ifdef _OPENMP 40 | #pragma omp parallel for schedule(dynamic) 41 | #endif 42 | for (int i = 0; i < max; ++i) { 43 | if ( p.increment() ) { // the only way to exit an OpenMP loop 44 | your_long_computation(nb); 45 | } 46 | } 47 | } 48 | 49 | RcppExport SEXP test_sequential_wrapper2(SEXP __max, SEXP __nb, SEXP __display_progress) { 50 | test_sequential2(as(__max), as(__nb), as(__display_progress)); 51 | return R_NilValue; 52 | } 53 | 54 | RcppExport SEXP test_multithreaded_wrapper2(SEXP __max, SEXP __nb, SEXP __threads, SEXP __display_progress) { 55 | test_multithreaded_omp2(as(__max), as(__nb), as(__threads), as(__display_progress)); 56 | return R_NilValue; 57 | } 58 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressArmadillo/src/example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "progress.hpp" 3 | 4 | 5 | using namespace Rcpp; 6 | 7 | // your function for which to provide support 8 | double your_long_computation(int nb) { 9 | double sum = 0; 10 | for (int i = 0; i < nb; ++i) { 11 | if ( Progress::check_abort() ) 12 | return -1; 13 | for (int j = 0; j < nb; ++j) { 14 | arma::mat m1 = arma::eye(3, 3); 15 | arma::mat m2 = arma::eye(3, 3); 16 | 17 | m1 = m1 + 3 * (m1 + m2); 18 | sum += m1[0]; 19 | } 20 | } 21 | 22 | return sum; 23 | } 24 | 25 | void test_sequential2(int max, int nb, bool display_progress) { 26 | 27 | Progress p(max, display_progress); 28 | for (int i = 0; i < max; ++i) { 29 | if ( p.increment() ) { 30 | your_long_computation(nb); 31 | } 32 | } 33 | } 34 | 35 | void test_multithreaded_omp2(int max, int nb, int threads, bool display_progress) { 36 | 37 | #ifdef _OPENMP 38 | if ( threads > 0 ) 39 | omp_set_num_threads( threads ); 40 | REprintf("Number of threads=%i\n", omp_get_max_threads()); 41 | #endif 42 | 43 | Progress p(max, display_progress); // create the progress monitor 44 | #ifdef _OPENMP 45 | #pragma omp parallel for schedule(dynamic) 46 | #endif 47 | for (int i = 0; i < max; ++i) { 48 | if ( p.increment() ) { // the only way to exit an OpenMP loop 49 | your_long_computation(nb); 50 | } 51 | } 52 | } 53 | 54 | RcppExport SEXP test_sequential_wrapper2(SEXP __max, SEXP __nb, SEXP __display_progress) { 55 | test_sequential2(as(__max), as(__nb), as(__display_progress)); 56 | return R_NilValue; 57 | } 58 | 59 | RcppExport SEXP test_multithreaded_wrapper2(SEXP __max, SEXP __nb, SEXP __threads, SEXP __display_progress) { 60 | test_multithreaded_omp2(as(__max), as(__nb), as(__threads), as(__display_progress)); 61 | return R_NilValue; 62 | } 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RcppProgress 2 | [![Build Status](https://travis-ci.org/kforner/rcpp_progress.svg?branch=dev)](https://travis-ci.org/kforner/rcpp_progress?branch=dev) 3 | [![codecov](https://codecov.io/github/kforner/rcpp_progress/coverage.svg?branch=dev)](https://codecov.io/github/kforner/rcpp_progress?branch=dev) 4 | [![CRAN_Status_Badge](http://www.r-pkg.org/badges/version/RcppProgress)](https://cran.r-project.org/package=RcppProgress) 5 | 6 | a R package that provides a c++ interruptible progress bar with OpenMP support for c++ code in R packages 7 | 8 | ## example 9 | see a detailed example on Rcpp Gallery: 10 | http://gallery.rcpp.org/articles/using-rcppprogress/ 11 | 12 | ## How to build 13 | 14 | Prerequisites: 15 | 16 | - OpenMP support to use the multithreaded parallelized version. OpenMP is available in GCC >= 4.2 17 | 18 | Just install it the usual way. 19 | 20 | If you want more control, unarchive it, cd to the source directory, then type 21 | R CMD INSTALL . in the console. 22 | 23 | 24 | ## Contribute 25 | Send me a pull request with at least one test or example 26 | 27 | 28 | ## For developers 29 | 30 | ### tests and check 31 | 32 | If you have all the RcppProgress dependencies (and suggests) installed: 33 | 34 | type: 35 | - `make tests`: to run the tests 36 | - `make check`: to check the package 37 | 38 | ### docker-checker 39 | 40 | A Dockerfile () is provided to help building the 41 | dev environment (built on rocker/r-devel) in which to develop 42 | and test RcppProgress. 43 | 44 | type: 45 | 46 | - `make docker/build`: to build the docker 47 | - `make docker/run`: to run a shell in the docker with the current dir mounted 48 | inside 49 | - `make docker/check`: to check the package inside the docker 50 | - `make docker/tests`: to run test tests of the package inside the docker 51 | 52 | ### test on windows using rhub 53 | 54 | ``` 55 | make docker/run 56 | make check_rhub_windows 57 | ``` 58 | 59 | 60 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | R=R 2 | RSCRIPT=Rscript 3 | VERSION=0.4 4 | RCHECKER=rcpp-rdevel 5 | NCPUS=4 6 | 7 | 8 | .PHONY: tests 9 | 10 | clean: 11 | rm -f src/*.o src/*.so */*~ *~ src/*.rds manual.pdf 12 | #rm -rf lib 13 | $(shell bash -c "shopt -s globstar && rm -f **/*.o **/*.so") 14 | 15 | lib: 16 | mkdir -p $@ 17 | 18 | install: lib 19 | $(R) -e 'pkg=devtools::build(".", "lib");install.packages(pkg, "lib")' 20 | 21 | coverage: 22 | $(R) -e 'covr::package_coverage()' 23 | 24 | # tests require an installed package 25 | tests: clean install 26 | R_LIBS=lib $(RSCRIPT) -e 'devtools::test()' 27 | 28 | test-RcppProgressArmadillo: install 29 | R CMD INSTALL inst/examples/RcppProgressArmadillo/ 30 | Rscript test_rcpp_armadillo_example.R 31 | 32 | debug-RcppProgressExample: install 33 | R_LIBS=lib $(RSCRIPT) -e 'devtools::load_all("inst/examples/RcppProgressExample", recompile = TRUE); RcppProgressExample:::test_multithreaded();' 34 | 35 | debug-RcppProgressETA: install 36 | R_LIBS=lib $(RSCRIPT) -e 'devtools::load_all("inst/examples/RcppProgressETA", recompile = TRUE); RcppProgressETA:::test_sequential();' 37 | 38 | 39 | build: 40 | $(R) CMD build . 41 | 42 | 43 | check: clean 44 | $(R) -q -e 'devtools::check()' 45 | 46 | # check with Rdevel 47 | check-rdev: clean 48 | $(R) -q -e 'devtools::check()' 49 | 50 | doc: 51 | $(R) CMD Rd2pdf -o manual.pdf . 52 | 53 | 54 | ################## docker checker ################################## 55 | # directory in which the local dir is mounted inside the container 56 | DIR=/root/rcpp_progress 57 | DOCKER_RUN=docker run --rm -ti -v $(PWD):$(PWD) -w $(PWD) -u $$(id -u):$$(id -g) $(RCHECKER) 58 | 59 | docker/build: 60 | docker build -t $(RCHECKER) docker_checker 61 | 62 | # check with r-base 63 | docker/check: 64 | #-docker rm $(RCHECKER) 65 | $(DOCKER_RUN) make check 66 | 67 | # check with r-devel 68 | docker/check-rdev: docker/build 69 | #-docker rm $(RCHECKER) 70 | $(DOCKER_RUN) make check-rdev 71 | 72 | docker/run: 73 | #@-docker rm $(RCHECKER) 74 | $(DOCKER_RUN) bash 75 | 76 | docker/tests: 77 | #@-docker rm $(RCHECKER) 78 | $(DOCKER_RUN) make tests 79 | 80 | test-r-devel: 81 | -docker rm $(RCHECKER) 82 | $(DOCKER_RUN) make tests 83 | 84 | 85 | check_rhub_windows: 86 | XDG_DATA_HOME=$(PWD) $(RSCRIPT) -e 'rhub::check_on_windows()' 87 | 88 | 89 | win-builder-upload: build 90 | lftp -u anonymous,karl.forner@gmail.com -e "set ftp:passive-mode true; cd R-release; mput *.tar.gz; cd ../R-devel; mput *.tar.gz; bye" ftp://win-builder.r-project.org 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /inst/include/simple_progress_bar.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * simple_progress_bar.hpp 3 | * 4 | * A class that display a progress bar 5 | * 6 | * Author: karl.forner@gmail.com 7 | * 8 | */ 9 | #ifndef _RcppProgress_SIMPLE_PROGRESS_BAR_HPP 10 | #define _RcppProgress_SIMPLE_PROGRESS_BAR_HPP 11 | 12 | #include "progress_bar.hpp" 13 | 14 | #include 15 | 16 | // for unices only 17 | #if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__) 18 | #include 19 | #endif 20 | 21 | class SimpleProgressBar: public ProgressBar{ 22 | public: // ====== LIFECYCLE ===== 23 | 24 | /** 25 | * Main constructor 26 | */ 27 | SimpleProgressBar() { reset(); } 28 | 29 | ~SimpleProgressBar() {} 30 | 31 | public: // ===== main methods ===== 32 | 33 | void display() { 34 | REprintf("0%% 10 20 30 40 50 60 70 80 90 100%%\n"); 35 | REprintf("[----|----|----|----|----|----|----|----|----|----|\n"); 36 | flush_console(); 37 | } 38 | 39 | // will finalize display if needed 40 | void update(float progress) { 41 | _update_ticks_display(progress); 42 | if (_ticks_displayed >= _max_ticks) 43 | _finalize_display(); 44 | } 45 | 46 | void end_display() { 47 | update(1); 48 | reset(); 49 | } 50 | 51 | void reset() { 52 | _max_ticks = 50; 53 | _ticks_displayed = 0; 54 | _finalized = false; 55 | } 56 | 57 | 58 | protected: // ==== other instance methods ===== 59 | 60 | // update the ticks display corresponding to progress 61 | void _update_ticks_display(float progress) { 62 | int nb_ticks = _compute_nb_ticks(progress); 63 | int delta = nb_ticks - _ticks_displayed; 64 | if (delta > 0) { 65 | _display_ticks(delta); 66 | _ticks_displayed = nb_ticks; 67 | } 68 | 69 | } 70 | 71 | void _finalize_display() { 72 | if (_finalized) return; 73 | 74 | REprintf("|\n"); 75 | flush_console(); 76 | _finalized = true; 77 | } 78 | 79 | int _compute_nb_ticks(float progress) { 80 | return int(progress * _max_ticks); 81 | } 82 | 83 | void _display_ticks(int nb) { 84 | for (int i = 0; i < nb; ++i) { 85 | REprintf("*"); 86 | R_FlushConsole(); 87 | } 88 | } 89 | 90 | // N.B: does nothing on windows 91 | void flush_console() { 92 | #if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__) 93 | R_FlushConsole(); 94 | #endif 95 | } 96 | 97 | private: 98 | int _max_ticks; // the total number of ticks to print 99 | int _ticks_displayed; // the nb of ticks already displayed 100 | bool _finalized; 101 | 102 | }; 103 | 104 | #endif 105 | -------------------------------------------------------------------------------- /inst/include/progress.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * progress.hpp 3 | * 4 | * A Front-end class for InterruptableProgressMonitor. 5 | * 6 | * Author: karl.forner@gmail.com 7 | * 8 | */ 9 | #ifndef _RcppProgress_PROGRESS_HPP 10 | #define _RcppProgress_PROGRESS_HPP 11 | 12 | #include "interruptable_progress_monitor.hpp" 13 | #include "simple_progress_bar.hpp" 14 | 15 | // e.g. for Rf_error 16 | #include 17 | 18 | 19 | class Progress { 20 | public: 21 | /** 22 | * 23 | * Main constructor 24 | * @param max the expected number of tasks to perform 25 | * @param display_progress whether to display a progress bar in the console 26 | * @param pb the ProgressBar instance to use 27 | 28 | */ 29 | Progress( 30 | unsigned long max, 31 | bool display_progress = true, 32 | ProgressBar& pb = default_progress_bar() 33 | ) { 34 | if ( monitor_singleton() != 0) { // something is wrong, two simultaneous Progress monitoring 35 | Rf_error("ERROR: there is already an InterruptableProgressMonitor instance defined"); 36 | } 37 | monitor_singleton() = new InterruptableProgressMonitor(max, display_progress, pb); 38 | } 39 | 40 | ~Progress() { cleanup(); } 41 | 42 | public: // ==== USER INTERFACE ===== 43 | /** 44 | * cleanup 45 | * 46 | * should normally not be called, unless a something bad happens ( 47 | * a process/thread that crashes). 48 | * 49 | */ 50 | void cleanup() { 51 | if (monitor_singleton() != 0) delete monitor_singleton(); 52 | monitor_singleton() = 0; 53 | } 54 | 55 | /** 56 | * increment the current progress. 57 | * 58 | * This method should preferably be used intead of update in a OpenMP context. 59 | * 60 | * Iff called by the master thread, it will also update the display if needed 61 | * 62 | * @param amount the number of newly performed tasks to report 63 | * 64 | * @return false iff the computation is aborted 65 | */ 66 | bool increment(unsigned long amount=1) { return monitor().increment(amount); } 67 | 68 | /** 69 | * set the current progress indicator 70 | * 71 | * Iff called by the master thread, it will also update the display if needed 72 | * 73 | * @param current the total number of performed tasks so far (by all threads) 74 | * 75 | * @return false iff the computation is aborted 76 | */ 77 | bool update(unsigned long current) { return monitor().update(current); } 78 | 79 | /** 80 | * return if the computation has been aborted. 81 | * N.B: do not perform any check by itselfd 82 | */ 83 | bool is_aborted() const { return monitor().is_aborted(); } 84 | 85 | /** 86 | * check that the no interruption has been requested and return the current status 87 | * 88 | * Iff called by the master thread, it will check for R-user level interruption. 89 | * 90 | * @return true iff the computation is aborted 91 | */ 92 | static bool check_abort() { return monitor().check_abort(); } 93 | 94 | private: 95 | static InterruptableProgressMonitor*& monitor_singleton() { 96 | static InterruptableProgressMonitor* p = 0; 97 | return p; 98 | } 99 | 100 | // trick to provide a default static member in a header file 101 | static SimpleProgressBar& default_progress_bar() { 102 | static SimpleProgressBar pb; 103 | pb.reset(); 104 | return pb; 105 | } 106 | 107 | public: // ==== OTHER PUBLIC INTERFACE ===== 108 | static InterruptableProgressMonitor& monitor() { return *monitor_singleton(); } 109 | 110 | }; 111 | 112 | #endif 113 | -------------------------------------------------------------------------------- /inst/examples/RcppProgressETA/src/eta_progress_bar.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * eta_progress_bar.hpp 3 | * 4 | * A custom ProgressBar class to display a progress bar with time estimation 5 | * 6 | * Author: clemens@nevrome.de 7 | * 8 | */ 9 | #ifndef _RcppProgress_ETA_PROGRESS_BAR_HPP 10 | #define _RcppProgress_ETA_PROGRESS_BAR_HPP 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "progress_bar.hpp" 19 | 20 | // for unices only 21 | #if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__) 22 | #include 23 | #endif 24 | 25 | class ETAProgressBar: public ProgressBar{ 26 | public: // ====== LIFECYCLE ===== 27 | 28 | /** 29 | * Main constructor 30 | */ 31 | ETAProgressBar() { 32 | _max_ticks = 50; 33 | _finalized = false; 34 | _timer_flag = true; 35 | } 36 | 37 | ~ETAProgressBar() { 38 | } 39 | 40 | public: // ===== main methods ===== 41 | 42 | void display() { 43 | REprintf("0%% 10 20 30 40 50 60 70 80 90 100%%\n"); 44 | REprintf("[----|----|----|----|----|----|----|----|----|----|\n"); 45 | flush_console(); 46 | } 47 | 48 | // update display 49 | void update(float progress) { 50 | 51 | // stop if already finalized 52 | if (_finalized) return; 53 | 54 | // start time measurement when update() is called the first time 55 | if (_timer_flag) { 56 | _timer_flag = false; 57 | // measure start time 58 | time(&start); 59 | } else { 60 | 61 | // measure current time 62 | time(&end); 63 | 64 | // calculate passed time and remaining time (in seconds) 65 | double pas_time = std::difftime(end, start); 66 | double rem_time = (pas_time / progress) * (1 - progress); 67 | 68 | // convert seconds to time string 69 | std::string time_string = _time_to_string(rem_time); 70 | 71 | // create progress bar string 72 | std::string progress_bar_string = _current_ticks_display(progress); 73 | 74 | // ensure overwriting of old time info 75 | int empty_length = time_string.length(); 76 | std::string empty_space = std::string(empty_length, ' '); 77 | 78 | // merge progress bar and time string 79 | std::stringstream strs; 80 | strs << "|" << progress_bar_string << "| " << time_string << empty_space; 81 | std::string temp_str = strs.str(); 82 | char const* char_type = temp_str.c_str(); 83 | 84 | // print: remove old and replace with new 85 | REprintf("\r"); 86 | REprintf("%s", char_type); 87 | 88 | // finalize display when ready 89 | if(progress == 1) { 90 | _finalize_display(); 91 | } 92 | } 93 | } 94 | 95 | void end_display() { 96 | update(1); 97 | } 98 | 99 | protected: // ==== other instance methods ===== 100 | 101 | // convert double with seconds to time string 102 | std::string _time_to_string(double seconds) { 103 | 104 | int time = (int) seconds; 105 | 106 | int hour = 0; 107 | int min = 0; 108 | int sec = 0; 109 | 110 | hour = time / 3600; 111 | time = time % 3600; 112 | min = time / 60; 113 | time = time % 60; 114 | sec = time; 115 | 116 | std::stringstream time_strs; 117 | if (hour != 0) time_strs << hour << "h "; 118 | if (min != 0) time_strs << min << "min "; 119 | if (sec != 0) time_strs << sec << "s "; 120 | std::string time_str = time_strs.str(); 121 | 122 | return time_str; 123 | } 124 | 125 | // update the ticks display corresponding to progress 126 | std::string _current_ticks_display(float progress) { 127 | 128 | int nb_ticks = _compute_nb_ticks(progress); 129 | 130 | std::string cur_display = _construct_ticks_display_string(nb_ticks); 131 | 132 | return cur_display; 133 | } 134 | 135 | // construct progress bar display 136 | std::string _construct_ticks_display_string(int nb) { 137 | 138 | std::stringstream ticks_strs; 139 | for (int i = 0; i < (_max_ticks - 1); ++i) { 140 | if (i < nb) { 141 | ticks_strs << "*"; 142 | } else { 143 | ticks_strs << " "; 144 | } 145 | } 146 | std::string tick_space_string = ticks_strs.str(); 147 | 148 | return tick_space_string; 149 | } 150 | 151 | // finalize 152 | void _finalize_display() { 153 | if (_finalized) return; 154 | 155 | REprintf("\n"); 156 | flush_console(); 157 | _finalized = true; 158 | } 159 | 160 | // compute number of ticks according to progress 161 | int _compute_nb_ticks(float progress) { 162 | return int(progress * _max_ticks); 163 | } 164 | 165 | // N.B: does nothing on windows 166 | void flush_console() { 167 | #if !defined(WIN32) && !defined(__WIN32) && !defined(__WIN32__) 168 | R_FlushConsole(); 169 | #endif 170 | } 171 | 172 | private: // ===== INSTANCE VARIABLES ==== 173 | int _max_ticks; // the total number of ticks to print 174 | bool _finalized; 175 | bool _timer_flag; 176 | time_t start,end; 177 | 178 | }; 179 | 180 | #endif 181 | -------------------------------------------------------------------------------- /inst/include/interruptable_progress_monitor.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * interruptable_progress_monitor.hpp 3 | * 4 | * A class that monitors the progress of computations: 5 | * - can display a progress bar 6 | * - can handle user interrupt (R user level) or programmatic abort 7 | * - can be used in OpenMP loops 8 | * 9 | * Author: karl.forner@gmail.com 10 | * 11 | */ 12 | #ifndef _RcppProgress_INTERRUPTABLE_PROGRESS_MONITOR_HPP 13 | #define _RcppProgress_INTERRUPTABLE_PROGRESS_MONITOR_HPP 14 | 15 | #include "interrupts.hpp" 16 | #include "progress_bar.hpp" 17 | //#include "fetch_raw_gwas_bar.hpp" 18 | 19 | #ifdef _OPENMP 20 | #include 21 | #endif 22 | 23 | class InterruptableProgressMonitor { 24 | public: // ====== LIFECYCLE ===== 25 | 26 | /** 27 | * Main constructor 28 | * 29 | * @param max the expected number of tasks to perform 30 | * @param display_progress whether to display a progress bar in the console 31 | * @param pb the ProgressBar instance to use 32 | */ 33 | InterruptableProgressMonitor( 34 | unsigned long max, 35 | bool display_progress, 36 | ProgressBar& pb 37 | ) : _progress_bar(pb) 38 | { 39 | reset(max, display_progress); 40 | if (is_display_on()) { 41 | _progress_bar.display(); 42 | } 43 | } 44 | 45 | ~InterruptableProgressMonitor() { 46 | if (is_display_on() && !is_aborted()) _progress_bar.end_display(); 47 | } 48 | 49 | public: // ===== ACCESSORS/SETTERS ===== 50 | void set_display_status(bool on) { _display_progress = on; } 51 | bool is_display_on() const { return _display_progress; } 52 | unsigned long get_max() const { return _max; } 53 | bool is_aborted() const { return _abort; } 54 | 55 | 56 | public: // ===== PBLIC MAIN INTERFACE ===== 57 | /** 58 | * increment the current progress. 59 | * 60 | * Iff called by the master thread, it will also update the display if needed 61 | * 62 | * @param amount the number of newly performed tasks to report 63 | * 64 | * @return false iff the computation is aborted 65 | */ 66 | bool increment(unsigned long amount=1) { 67 | if ( is_aborted() ) 68 | return false; 69 | return is_master() ? update_master(_current + amount) : atomic_increment(amount); 70 | } 71 | 72 | /** 73 | * set the current progress indicator 74 | * 75 | * Iff called by the master thread, it will also update the display if needed 76 | * 77 | * @param current the total number of performed tasks so far (by all threads) 78 | * 79 | * @return false iff the computation is aborted 80 | */ 81 | bool update(unsigned long current) { 82 | if ( is_aborted() ) 83 | return false; 84 | return is_master() ? update_master(current) : atomic_update(current); 85 | } 86 | 87 | /** 88 | * check that the no interruption has been requested and return the current status 89 | * 90 | * Iff called by the master thread, it will check for R-user level interruption. 91 | * 92 | * @return true iff the computation is aborted 93 | */ 94 | bool check_abort() { 95 | if ( is_aborted() ) 96 | return true; 97 | 98 | if ( is_master() ) { 99 | check_user_interrupt_master(); 100 | } 101 | return is_aborted(); 102 | } 103 | 104 | /** 105 | * request computation abortion 106 | */ 107 | void abort() { 108 | #ifdef _OPENMP 109 | #pragma omp critical 110 | #endif 111 | _abort = true; 112 | 113 | } 114 | 115 | /** 116 | * return true iff the thread is the master. 117 | * In case of non-OpenMP loop, always return true 118 | */ 119 | bool is_master() const { 120 | #ifdef _OPENMP 121 | return omp_get_thread_num() == 0; 122 | #else 123 | return true; 124 | #endif 125 | } 126 | 127 | public: // ===== methods for MASTER thread ===== 128 | 129 | /** 130 | * set the current progress indicator and update the progress bar display if needed. 131 | * 132 | * 133 | * @param current the total number of performed tasks 134 | * 135 | * @return false iff the computation is aborted 136 | */ 137 | bool update_master(unsigned long current) { 138 | _current = current; 139 | if (is_display_on()) _progress_bar.update(progress(current)); 140 | return ! is_aborted(); 141 | } 142 | 143 | void check_user_interrupt_master() { 144 | if ( !is_aborted() && checkInterrupt() ) { 145 | abort(); 146 | } 147 | } 148 | 149 | public: // ===== methods for non-MASTER threads ===== 150 | 151 | bool atomic_increment(unsigned long amount=1) { 152 | #ifdef _OPENMP 153 | #pragma omp atomic 154 | #endif 155 | _current+=amount; 156 | return ! is_aborted(); 157 | } 158 | 159 | bool atomic_update(unsigned long current) { 160 | #ifdef _OPENMP 161 | #pragma omp critical 162 | #endif 163 | _current=current; 164 | return ! is_aborted(); 165 | } 166 | 167 | protected: // ==== other instance methods ===== 168 | // convert current value to [0-1] progress 169 | double progress(unsigned long current) { 170 | return double(current) / double(_max); 171 | } 172 | 173 | /** 174 | * reset the monitor. 175 | * 176 | * Currently not really useful 177 | * 178 | * @param max the expected number of tasks to perform 179 | * @param display_progress whether to display a progress bar in the console 180 | * 181 | */ 182 | void reset(unsigned long max = 1, bool display_progress = true) { 183 | _max = max; 184 | if ( _max <= 0 ) 185 | _max = 1; 186 | _current = 0; 187 | _display_progress = display_progress; 188 | _abort = false; 189 | } 190 | 191 | 192 | private: // ===== INSTANCE VARIABLES ==== 193 | ProgressBar& _progress_bar; 194 | unsigned long _max; // the nb of tasks to perform 195 | unsigned long _current; // the current nb of tasks performed 196 | 197 | bool _abort; // whether the process should abort 198 | bool _display_progress; // whether to display the progress bar 199 | 200 | }; 201 | 202 | #endif 203 | -------------------------------------------------------------------------------- /man/RcppProgress-package.Rd: -------------------------------------------------------------------------------- 1 | \name{RcppProgress-package} 2 | \alias{RcppProgress-package} 3 | \alias{RcppProgress} 4 | \docType{package} 5 | \title{ 6 | An interruptible progress bar with OpenMP support for c++ in R packages 7 | } 8 | \description{ 9 | This package allows to display a progress bar in the R 10 | console for long running computations taking place in c++ code, 11 | and provides support for interrupting those computations even in a multithreaded code. 12 | } 13 | 14 | \details{ 15 | 16 | When implementing CPU intensive computations in C++ in R packages, it is natural to want to monitor 17 | the progress of those computations, and to be able to interrupt them, even when using 18 | multithreading for example with OpenMP. 19 | Another feature is that it can be done so that the code will still work even without OpenMP support. 20 | This package offers some facilities to help implementing those features. 21 | It it biased towards the use of OpenMP, but it should be compatible when using 22 | multithreading in other ways. 23 | 24 | \subsection{quick try}{ 25 | There are two tests functions provided by the package to get a quick overview 26 | of what can be done. 27 | 28 | These tests are: 29 | 30 | \describe{ 31 | \item{ RcppProgress:::test_sequential(max, nb, display_progress) }{ - a sequential code, i.e. single threaded } 32 | \item{ RcppProgress:::test_multithreaded(max, nb, threads, display_progress) }{ - a multithreaded code using OpenMP} 33 | } 34 | 35 | They both are wrappers for examples implemented in the RcppProgressExample package 36 | located in the \code{examples} directory of the RcppProgress installed package. 37 | 38 | 39 | Both tests call the very same function that implements a long computation. 40 | The \bold{max} parameter controls the number of computations, and \bold{nb} controls the duration of a single computation, 41 | that is quadratic in \bold{nb}. 42 | The \bold{threads} is as expected the number of threads to use for the computation. 43 | The \bold{progress} parameter toggles the display of the progress bar. 44 | 45 | On my platform, 46 | \preformatted{ 47 | system.time( RcppProgress:::test_multithreaded(100, 3000, 4) ) 48 | } 49 | is a good start. 50 | 51 | } 52 | 53 | \subsection{c++ usage}{ 54 | 55 | There are usually two locations in the c++ code that needs to be modified. 56 | The first one is the main loop, typically on the number of jobs or tasks. This loop is a good candidate to 57 | be parallelized using OpenMP. 58 | I will comment the code corresponding to the tests included with the package. 59 | 60 | 61 | \preformatted{ 62 | void test_multithreaded_omp(int max, int nb, int threads 63 | , bool display\_progress) \{ 64 | 65 | \#ifdef _OPENMP 66 | if ( threads > 0 ) 67 | omp_set_num_threads( threads ); 68 | REprintf(\"Number of threads=\%i\n\", omp_get_max_threads()); 69 | \#endif 70 | 71 | Progress p(max, display_progress); // create the progress monitor 72 | #pragma omp parallel for schedule(dynamic) 73 | for (int i = 0; i < max; ++i) \{ 74 | if ( ! p.is_aborted() ) \{ // the only way to exit an OpenMP loop 75 | long_computation(nb); 76 | p.increment(); // update the progress 77 | \} 78 | \} 79 | \} 80 | } 81 | 82 | Here we create a Progress object with the number of tasks to perform, then 83 | before performing a task we test for abortion (\code{p.is_aborted()}), because we can not exit an 84 | OpenMP loop the usual way, suing a break for example, then 85 | when after the computation, we increment the progress monitor. 86 | 87 | Then let us look at the computation function (that is completely useless) : 88 | 89 | \preformatted{ 90 | double long_computation(int nb) \{ 91 | double sum = 0; 92 | for (int i = 0; i < nb; ++i) \{ 93 | if ( Progress::check_abort() ) // check for user abort 94 | return -1; 95 | for (int j = 0; j < nb; ++j) \{ 96 | sum += Rf_dlnorm(i+j, 0.0, 1.0, 0); 97 | \} 98 | \} 99 | \} 100 | return sum; 101 | \} 102 | } 103 | 104 | Here the only interesting line is the \code{Progress::check_abort()} call. 105 | If called from the master thread, it will check for user interruption, and if needed 106 | set the abort status code. 107 | When called from another thread it will just check the status. 108 | So all the art is to decide where to put his call: it should not be called not too often 109 | or not frequently enough. 110 | As a rule of thumb it should be called roughly evevry second. 111 | 112 | } 113 | 114 | \subsection{Using RcppProgress in your package}{ 115 | 116 | Please note that we provide the \bold{RcppProgressExample} example package along with this package, 117 | located in the \code{examples} directory of the installed package. 118 | 119 | Here are the steps to use RcppProgress in a new package: 120 | 121 | \describe{ 122 | 123 | \item{ skeleton }{ 124 | create a package skeleton using Rcpp 125 | \preformatted{ 126 | 127 | library(Rcpp) 128 | Rcpp.package.skeleton("RcppProgressExample")} 129 | } 130 | 131 | \item{ DESCRIPTION }{ edit the \bold{DESCRIPTION} file and add RcppProgress to 132 | the \strong{Depends:} and \strong{LinkingTo:} lines. e.g. 133 | \preformatted{ 134 | Depends: Rcpp (>= 0.9.4), RcppProgress (>= 0.1) 135 | LinkingTo: Rcpp, RcppProgress 136 | } 137 | } 138 | 139 | \item{ MakeVars }{ edit \bold{src/MakeVars} and replace its content by 140 | 141 | PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(SHLIB_OPENMP_CXXFLAGS) `$(R_HOME)/bin/Rscript -e "RcppProgress:::CxxFlags()"` 142 | and 143 | 144 | PKG_CXXFLAGS +=-Ilibsrc $(SHLIB_OPENMP_CXXFLAGS) `$(R_HOME)/bin/Rscript -e "RcppProgress:::CxxFlags()"` 145 | 146 | } 147 | 148 | \item{ c++ code }{ 149 | Put your code in \bold{src}. 150 | You may for instance copy the RcppProgressExample/src/tests.cpp file in \bold{src}, and RcppProgressExample/R/tests.R in 151 | \bold{R}, and try to 152 | compile the package (\code{R CMD INSTALL -l test .}) and execute the tests: 153 | 154 | \preformatted{ 155 | %R 156 | >library(RcppProgressExample, lib.loc="test") 157 | >RcppProgressExample::test_multithreaded(100, 600, 4) 158 | } 159 | } 160 | 161 | } 162 | } % subsection 163 | 164 | 165 | \subsection{Using RcppProgress with RcppArmadillo}{ 166 | We also provide the \bold{RcppProgressArmadillo} example package along with this package, 167 | located in the \code{examples} directory of the installed package. 168 | 169 | The peculiarity is that you have to include the RcppArmadillo.h header before the 170 | progress.hpp RcppProgress header, and add the RcppArmadillo in the LinkingTo: 171 | field of the package DESCRIPTION file. 172 | 173 | 174 | } % subsection 175 | 176 | 177 | } % details 178 | 179 | 180 | \seealso{ 181 | \describe{ 182 | 183 | \item{ OpenMP }{API specification for parallel programming: \url{http://openmp.org} } 184 | 185 | \item{ Rcpp }{\url{http://r-forge.r-project.org/projects/rcpp}} 186 | } 187 | 188 | } 189 | 190 | \author{ 191 | Karl Forner 192 | 193 | Maintainer: Karl Forner 194 | } 195 | 196 | \examples{ 197 | # these are implemented as examples inside RcppProgress provided 198 | # example package: examples/RcppProgressExample 199 | # check the source code 200 | 201 | # the underlying test_test_multithreaded c++ function is multithreaded 202 | # , has a progress bar and is still interruptible 203 | \dontrun{ 204 | RcppProgress:::test_multithreaded(nb = 300, threads = 4, recompile = TRUE) 205 | } 206 | } 207 | 208 | \keyword{ package } 209 | 210 | --------------------------------------------------------------------------------