diff --git a/.Rbuildignore b/.Rbuildignore index 91114bf..6d239cd 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -1,2 +1,15 @@ -^.*\.Rproj$ -^\.Rproj\.user$ +.vscode +.devcontainer +.dev +^\.github$ +^_pkgdown\.yml$ +^docs$ +^pkgdown$ +^man-roxygen +src/test.cpp +cran-comments.md +^CRAN-RELEASE$ +^LICENSE\.md$ +^CRAN-SUBMISSION$ +inst/include/cpptimer/.git +^cran-comments\.md$ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..08f0136 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,75 @@ +{ + "name": "DevEnv", + "image": "ghcr.io/berrij/devenv:latest", + // Grant xhost access to root user in order to start x11 devices (r plots) + "initializeCommand": "docker pull ghcr.io/berrij/devenv:latest", + "customizations": { + "vscode": { + // Extensions will get installed automatically + "extensions": [ + // C++ + "ms-vscode.cpptools-extension-pack", + // Python + "ms-python.vscode-pylance", + "ms-python.python", + "ms-python.black-formatter", + "visualstudioexptteam.vscodeintellicode", + "ms-toolsai.jupyter", + // R + "REditorSupport.r", + "rdebugger.r-debugger", + // Latex + "james-yu.latex-workshop", + "znck.grammarly", + // Git + "mhutchie.git-graph", + "github.vscode-pull-request-github", + // Markdown + "yzhang.markdown-all-in-one", + // Preview HTML + "daiyy.quick-html-previewer", + // Make fancy screenshots + "jeffersonlicet.snipped", + // Live Share + "ms-vsliveshare.vsliveshare", + // Copilot + "GitHub.copilot" + ], + // Set *default* container specific settings + // Important for radian and latex + "settings": { + // C++ + "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", + "cmake.configureSettings": { + "CMAKE_TOOLCHAIN_FILE": "/usr/vcpkg/scripts/buildsystems/vcpkg.cmake" + }, + // Python + "jupyter.notebookFileRoot": "${workspaceFolder}", + "python.pythonPath": "/usr/bin/python3", + "python.dataScience.interactiveWindowMode": "perFile", + "python.dataScience.sendSelectionToInteractiveWindow": true, + // R + "r.rterm.linux": "/usr/local/bin/radian", + "r.bracketedPaste": true, + "r.sessionWatcher": true, + "r.plot.useHttpgd": true, + "r.plot.defaults.fullWindowMode": true, + // Latex + "latex-workshop.latex.recipe.default": "latexmk (lualatex)", + "latex-workshop.latex.autoBuild.run": "onFileChange", + "latex-workshop.view.pdf.viewer": "tab", + "latex-workshop.linting.chktex.enabled": true, + "latex-workshop.linting.run": "onType", + "latex-workshop.synctex.afterBuild.enabled": true, + "latex-workshop.view.pdf.internal.synctex.keybinding": "double-click", + "latex-workshop.intellisense.unimathsymbols.enabled": true, + // Format code as you type + "editor.formatOnPaste": true, + "editor.formatOnType": true, + "editor.formatOnSave": true, + // Hide .gitignore files + "explorer.excludeGitIgnore": true + } + } + } +} \ No newline at end of file diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..2d19fc7 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +*.html diff --git a/.github/workflows/R-CMD-check-Dev.yaml b/.github/workflows/R-CMD-check-Dev.yaml new file mode 100644 index 0000000..d1388aa --- /dev/null +++ b/.github/workflows/R-CMD-check-Dev.yaml @@ -0,0 +1,55 @@ +# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. +# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions +on: [push, pull_request] + +name: R-CMD-check-Dev + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macOS-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + + - name: Show testthat output + if: always() + run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload check results + if: failure() + uses: actions/upload-artifact@main + with: + name: ${{ runner.os }}-r${{ matrix.config.r }}-results + path: check \ No newline at end of file diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml new file mode 100644 index 0000000..df6f85f --- /dev/null +++ b/.github/workflows/R-CMD-check.yaml @@ -0,0 +1,56 @@ +# For help debugging build failures open an issue on the RStudio community with the 'github-actions' tag. +# https://community.rstudio.com/new-topic?category=Package%20development&tags=github-actions +on: + pull_request + +name: R-CMD-check + +jobs: + R-CMD-check: + runs-on: ${{ matrix.config.os }} + + name: ${{ matrix.config.os }} (${{ matrix.config.r }}) + + strategy: + fail-fast: false + matrix: + config: + - {os: macOS-latest, r: 'release'} + - {os: windows-latest, r: 'release'} + - {os: ubuntu-latest, r: 'release'} + + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes + + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::rcmdcheck + needs: check + + - uses: r-lib/actions/check-r-package@v2 + + - name: Show testthat output + if: always() + run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true + shell: bash + + - name: Upload check results + if: failure() + uses: actions/upload-artifact@main + with: + name: ${{ runner.os }}-r${{ matrix.config.r }}-results + path: check \ No newline at end of file diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml new file mode 100644 index 0000000..8b3758e --- /dev/null +++ b/.github/workflows/pkgdown.yaml @@ -0,0 +1,41 @@ +on: push + +name: pkgdown + +jobs: + pkgdown: + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} + env: + GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write + steps: + - uses: actions/checkout@v3 + with: + submodules: 'true' + + - uses: r-lib/actions/setup-pandoc@v2 + + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true + + - uses: r-lib/actions/setup-r-dependencies@v2 + with: + extra-packages: any::pkgdown, local::. + needs: website + + - name: Build site + run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) + shell: Rscript {0} + + - name: Deploy to GitHub pages 🚀 + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.4.1 + with: + clean: false + branch: gh-pages + folder: docs \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..200ecc4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +.dev/* +docs +src/*.o +src/*.so +src/*.dll +src/test.cpp +.vscode/.* +inst/doc diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..852c69c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "inst/include/cpptimer"] + path = inst/include/cpptimer + url = git@github.com:BerriJ/cpptimer.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..bcb392a --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,22 @@ +{ + "configurations": [ + { + "name": "Linux", + "includePath": [ + "${workspaceFolder}/**", + "/usr/local/lib/R/site-library/Rcpp/include", + "/usr/local/lib/R/site-library/RcppArmadillo/include", + "/usr/local/lib/R/site-library/RcppProgress/include", + "/usr/local/lib/R/site-library/splines2/include", + "/usr/share/R/include" + ], + "defines": [], + "compilerPath": "/usr/bin/gcc", + "cStandard": "gnu17", + "cppStandard": "gnu++14", + "intelliSenseMode": "linux-gcc-x64", + "configurationProvider": "ms-vscode.cmake-tools" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/debug.R b/.vscode/debug.R new file mode 100644 index 0000000..c7e8ac0 --- /dev/null +++ b/.vscode/debug.R @@ -0,0 +1,9 @@ +env <- Sys.getenv() +envnames <- names(env) +rnames <- envnames[startsWith(envnames, "R_")] +cached_names <- rnames +ld_lib_path <- Sys.getenv("LD_LIBRARY_PATH") +if (ld_lib_path != "") { + cached_names <- c("LD_LIBRARY_PATH", rnames) +} +writeLines(paste0(cached_names, "=", env[cached_names]), ".vscode/.env") \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..06dd809 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,45 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Debug RCPP", + "type": "cppdbg", + "request": "launch", + "program": "/usr/lib/R/bin/exec/R", + "args": [ + "--vanilla", + "-e", + "devtools::test()" + ], + "stopAtEntry": false, + "envFile": "${workspaceFolder}/.vscode/.env", + "cwd": "${workspaceFolder}", + "externalConsole": false, + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ], + "preLaunchTask": "debug", + "osx": { + "program": "/Library/Frameworks/R.framework/Resources/bin/exec/R", + "MIMode": "lldb" + } + }, + { + "type": "R-Debugger", + "name": "Debug R-Package", + "request": "launch", + "debugMode": "workspace", + "workingDirectory": "${workspaceFolder}", + "loadPackages": [ + "." + ], + "includePackageScopes": true, + "allowGlobalDebugging": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..375f7c5 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,84 @@ +{ + "files.associations": { + "*.tcc": "cpp", + "deque": "cpp", + "string": "cpp", + "vector": "cpp", + "ostream": "cpp", + "array": "cpp", + "unordered_map": "cpp", + "string_view": "cpp", + "initializer_list": "cpp", + "random": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "exception": "cpp", + "algorithm": "cpp", + "functional": "cpp", + "iterator": "cpp", + "memory": "cpp", + "memory_resource": "cpp", + "numeric": "cpp", + "optional": "cpp", + "system_error": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "utility": "cpp", + "fstream": "cpp", + "iosfwd": "cpp", + "istream": "cpp", + "limits": "cpp", + "new": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "streambuf": "cpp", + "typeinfo": "cpp", + "armadillo": "cpp", + "csetjmp": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "chrono": "cpp", + "complex": "cpp", + "condition_variable": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "unordered_set": "cpp", + "ratio": "cpp", + "iomanip": "cpp", + "iostream": "cpp", + "mutex": "cpp", + "thread": "cpp", + "cinttypes": "cpp", + "strstream": "cpp", + "bitset": "cpp", + "cfenv": "cpp", + "typeindex": "cpp", + "valarray": "cpp", + "variant": "cpp", + "hash_map": "cpp", + "compare": "cpp", + "concepts": "cpp", + "numbers": "cpp", + "semaphore": "cpp", + "stop_token": "cpp", + "forward_list": "cpp" + }, + "grammarly.selectors": [ + { + "language": "rmd", + "scheme": "file", + "pattern": "vignettes/Introduction.Rmd" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..8376613 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,13 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "debug", + "type": "shell", + "command": "Rscript ${workspaceFolder}/.vscode/debug.R", + "problemMatcher": [] + } + ] +} diff --git a/CRAN-SUBMISSION b/CRAN-SUBMISSION new file mode 100644 index 0000000..5619695 --- /dev/null +++ b/CRAN-SUBMISSION @@ -0,0 +1,3 @@ +Version: 1.0.0 +Date: 2024-03-01 20:00:09 UTC +SHA: 661f935d8c63eb220f5880575af1515b3cc6ae43 diff --git a/DESCRIPTION b/DESCRIPTION index e81f08a..dcb30ae 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,15 +1,23 @@ -Package: RcppClock -Type: Package -Title: Seamless 'Rcpp' Benchmarking -Version: 1.2 -Date: 2021-11-24 -Author: Zach DeBruine -Maintainer: Zach DeBruine -Description: Time the execution of overlapping (or not) 'Rcpp' code chunks using convenient methods, seamlessly write timing results to an 'RcppClock' object in the R global environment, and summarize and/or plot the results in R using 'RcppClock' methods. -License: GPL (>= 2) -Imports: Rcpp (>= 1.0.7), ggplot2 -LinkingTo: Rcpp -RoxygenNote: 7.1.2 -Suggests: - testthat (>= 3.0.0) -Config/testthat/edition: 3 +Package: rcpptimer +Type: Package +Title: 'Rcpp' Tic-Toc Timer with 'OpenMP' Support +Version: 1.0.0 +Date: 2024-02-29 +Authors@R: c( + person(given = "Jonathan", + family = "Berrisch", + role = c("aut", "cre"), + email = "Jonathan@Berrisch.biz", + comment = c(ORCID = "0000-0002-4944-9074"))) +Description: Provides 'Rcpp' bindings for 'cpptimer', a simple tic-toc timer class for benchmarking 'C++' code . It's not just simple, it's blazing fast! This sleek tic-toc timer class supports overlapping timers as well as 'OpenMP' parallelism . It boasts a microsecond-level time resolution. We did not find any overhead of the timer itself at this resolution. Results (with summary statistics) are automatically passed back to 'R' as a data frame. +URL: https://rcpptimer.berrisch.biz +License: GPL (>= 3) +Encoding: UTF-8 +Imports: Rcpp +LinkingTo: Rcpp +RoxygenNote: 7.3.1 +Suggests: + testthat (>= 3.0.0) +Config/testthat/edition: 3 +Roxygen: list(markdown = TRUE) +Language: en-US diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..175443c --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,595 @@ +GNU General Public License +========================== + +_Version 3, 29 June 2007_ +_Copyright © 2007 Free Software Foundation, Inc. <>_ + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +## Preamble + +The GNU General Public License is a free, copyleft license for software and other +kinds of works. + +The licenses for most software and other practical works are designed to take away +your freedom to share and change the works. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change all versions of a +program--to make sure it remains free software for all its users. We, the Free +Software Foundation, use the GNU General Public License for most of our software; it +applies also to any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our General +Public Licenses are designed to make sure that you have the freedom to distribute +copies of free software (and charge for them if you wish), that you receive source +code or can get it if you want it, that you can change the software or use pieces of +it in new free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you these rights or +asking you to surrender the rights. Therefore, you have certain responsibilities if +you distribute copies of the software, or if you modify it: responsibilities to +respect the freedom of others. + +For example, if you distribute copies of such a program, whether gratis or for a fee, +you must pass on to the recipients the same freedoms that you received. You must make +sure that they, too, receive or can get the source code. And you must show them these +terms so they know their rights. + +Developers that use the GNU GPL protect your rights with two steps: **(1)** assert +copyright on the software, and **(2)** offer you this License giving you legal permission +to copy, distribute and/or modify it. + +For the developers' and authors' protection, the GPL clearly explains that there is +no warranty for this free software. For both users' and authors' sake, the GPL +requires that modified versions be marked as changed, so that their problems will not +be attributed erroneously to authors of previous versions. + +Some devices are designed to deny users access to install or run modified versions of +the software inside them, although the manufacturer can do so. This is fundamentally +incompatible with the aim of protecting users' freedom to change the software. The +systematic pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we have designed +this version of the GPL to prohibit the practice for those products. If such problems +arise substantially in other domains, we stand ready to extend this provision to +those domains in future versions of the GPL, as needed to protect the freedom of +users. + +Finally, every program is threatened constantly by software patents. States should +not allow patents to restrict development and use of software on general-purpose +computers, but in those that do, we wish to avoid the special danger that patents +applied to a free program could make it effectively proprietary. To prevent this, the +GPL assures that patents cannot be used to render the program non-free. + +The precise terms and conditions for copying, distribution and modification follow. + +## TERMS AND CONDITIONS + +### 0. Definitions + +“This License” refers to version 3 of the GNU General Public License. + +“Copyright” also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + +“The Program” refers to any copyrightable work licensed under this +License. Each licensee is addressed as “you”. “Licensees” and +“recipients” may be individuals or organizations. + +To “modify” a work means to copy from or adapt all or part of the work in +a fashion requiring copyright permission, other than the making of an exact copy. The +resulting work is called a “modified version” of the earlier work or a +work “based on” the earlier work. + +A “covered work” means either the unmodified Program or a work based on +the Program. + +To “propagate” a work means to do anything with it that, without +permission, would make you directly or secondarily liable for infringement under +applicable copyright law, except executing it on a computer or modifying a private +copy. Propagation includes copying, distribution (with or without modification), +making available to the public, and in some countries other activities as well. + +To “convey” a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through a computer +network, with no transfer of a copy, is not conveying. + +An interactive user interface displays “Appropriate Legal Notices” to the +extent that it includes a convenient and prominently visible feature that **(1)** +displays an appropriate copyright notice, and **(2)** tells the user that there is no +warranty for the work (except to the extent that warranties are provided), that +licensees may convey the work under this License, and how to view a copy of this +License. If the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + +### 1. Source Code + +The “source code” for a work means the preferred form of the work for +making modifications to it. “Object code” means any non-source form of a +work. + +A “Standard Interface” means an interface that either is an official +standard defined by a recognized standards body, or, in the case of interfaces +specified for a particular programming language, one that is widely used among +developers working in that language. + +The “System Libraries” of an executable work include anything, other than +the work as a whole, that **(a)** is included in the normal form of packaging a Major +Component, but which is not part of that Major Component, and **(b)** serves only to +enable use of the work with that Major Component, or to implement a Standard +Interface for which an implementation is available to the public in source code form. +A “Major Component”, in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system (if any) on which +the executable work runs, or a compiler used to produce the work, or an object code +interpreter used to run it. + +The “Corresponding Source” for a work in object code form means all the +source code needed to generate, install, and (for an executable work) run the object +code and to modify the work, including scripts to control those activities. However, +it does not include the work's System Libraries, or general-purpose tools or +generally available free programs which are used unmodified in performing those +activities but which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for the work, and +the source code for shared libraries and dynamically linked subprograms that the work +is specifically designed to require, such as by intimate data communication or +control flow between those subprograms and other parts of the work. + +The Corresponding Source need not include anything that users can regenerate +automatically from other parts of the Corresponding Source. + +The Corresponding Source for a work in source code form is that same work. + +### 2. Basic Permissions + +All rights granted under this License are granted for the term of copyright on the +Program, and are irrevocable provided the stated conditions are met. This License +explicitly affirms your unlimited permission to run the unmodified Program. The +output from running a covered work is covered by this License only if the output, +given its content, constitutes a covered work. This License acknowledges your rights +of fair use or other equivalent, as provided by copyright law. + +You may make, run and propagate covered works that you do not convey, without +conditions so long as your license otherwise remains in force. You may convey covered +works to others for the sole purpose of having them make modifications exclusively +for you, or provide you with facilities for running those works, provided that you +comply with the terms of this License in conveying all material for which you do not +control copyright. Those thus making or running the covered works for you must do so +exclusively on your behalf, under your direction and control, on terms that prohibit +them from making any copies of your copyrighted material outside their relationship +with you. + +Conveying under any other circumstances is permitted solely under the conditions +stated below. Sublicensing is not allowed; section 10 makes it unnecessary. + +### 3. Protecting Users' Legal Rights From Anti-Circumvention Law + +No covered work shall be deemed part of an effective technological measure under any +applicable law fulfilling obligations under article 11 of the WIPO copyright treaty +adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention +of such measures. + +When you convey a covered work, you waive any legal power to forbid circumvention of +technological measures to the extent such circumvention is effected by exercising +rights under this License with respect to the covered work, and you disclaim any +intention to limit operation or modification of the work as a means of enforcing, +against the work's users, your or third parties' legal rights to forbid circumvention +of technological measures. + +### 4. Conveying Verbatim Copies + +You may convey verbatim copies of the Program's source code as you receive it, in any +medium, provided that you conspicuously and appropriately publish on each copy an +appropriate copyright notice; keep intact all notices stating that this License and +any non-permissive terms added in accord with section 7 apply to the code; keep +intact all notices of the absence of any warranty; and give all recipients a copy of +this License along with the Program. + +You may charge any price or no price for each copy that you convey, and you may offer +support or warranty protection for a fee. + +### 5. Conveying Modified Source Versions + +You may convey a work based on the Program, or the modifications to produce it from +the Program, in the form of source code under the terms of section 4, provided that +you also meet all of these conditions: + +* **a)** The work must carry prominent notices stating that you modified it, and giving a +relevant date. +* **b)** The work must carry prominent notices stating that it is released under this +License and any conditions added under section 7. This requirement modifies the +requirement in section 4 to “keep intact all notices”. +* **c)** You must license the entire work, as a whole, under this License to anyone who +comes into possession of a copy. This License will therefore apply, along with any +applicable section 7 additional terms, to the whole of the work, and all its parts, +regardless of how they are packaged. This License gives no permission to license the +work in any other way, but it does not invalidate such permission if you have +separately received it. +* **d)** If the work has interactive user interfaces, each must display Appropriate Legal +Notices; however, if the Program has interactive interfaces that do not display +Appropriate Legal Notices, your work need not make them do so. + +A compilation of a covered work with other separate and independent works, which are +not by their nature extensions of the covered work, and which are not combined with +it such as to form a larger program, in or on a volume of a storage or distribution +medium, is called an “aggregate” if the compilation and its resulting +copyright are not used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work in an aggregate +does not cause this License to apply to the other parts of the aggregate. + +### 6. Conveying Non-Source Forms + +You may convey a covered work in object code form under the terms of sections 4 and +5, provided that you also convey the machine-readable Corresponding Source under the +terms of this License, in one of these ways: + +* **a)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by the Corresponding Source fixed on a +durable physical medium customarily used for software interchange. +* **b)** Convey the object code in, or embodied in, a physical product (including a +physical distribution medium), accompanied by a written offer, valid for at least +three years and valid for as long as you offer spare parts or customer support for +that product model, to give anyone who possesses the object code either **(1)** a copy of +the Corresponding Source for all the software in the product that is covered by this +License, on a durable physical medium customarily used for software interchange, for +a price no more than your reasonable cost of physically performing this conveying of +source, or **(2)** access to copy the Corresponding Source from a network server at no +charge. +* **c)** Convey individual copies of the object code with a copy of the written offer to +provide the Corresponding Source. This alternative is allowed only occasionally and +noncommercially, and only if you received the object code with such an offer, in +accord with subsection 6b. +* **d)** Convey the object code by offering access from a designated place (gratis or for +a charge), and offer equivalent access to the Corresponding Source in the same way +through the same place at no further charge. You need not require recipients to copy +the Corresponding Source along with the object code. If the place to copy the object +code is a network server, the Corresponding Source may be on a different server +(operated by you or a third party) that supports equivalent copying facilities, +provided you maintain clear directions next to the object code saying where to find +the Corresponding Source. Regardless of what server hosts the Corresponding Source, +you remain obligated to ensure that it is available for as long as needed to satisfy +these requirements. +* **e)** Convey the object code using peer-to-peer transmission, provided you inform +other peers where the object code and Corresponding Source of the work are being +offered to the general public at no charge under subsection 6d. + +A separable portion of the object code, whose source code is excluded from the +Corresponding Source as a System Library, need not be included in conveying the +object code work. + +A “User Product” is either **(1)** a “consumer product”, which +means any tangible personal property which is normally used for personal, family, or +household purposes, or **(2)** anything designed or sold for incorporation into a +dwelling. In determining whether a product is a consumer product, doubtful cases +shall be resolved in favor of coverage. For a particular product received by a +particular user, “normally used” refers to a typical or common use of +that class of product, regardless of the status of the particular user or of the way +in which the particular user actually uses, or expects or is expected to use, the +product. A product is a consumer product regardless of whether the product has +substantial commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + +“Installation Information” for a User Product means any methods, +procedures, authorization keys, or other information required to install and execute +modified versions of a covered work in that User Product from a modified version of +its Corresponding Source. The information must suffice to ensure that the continued +functioning of the modified object code is in no case prevented or interfered with +solely because modification has been made. + +If you convey an object code work under this section in, or with, or specifically for +use in, a User Product, and the conveying occurs as part of a transaction in which +the right of possession and use of the User Product is transferred to the recipient +in perpetuity or for a fixed term (regardless of how the transaction is +characterized), the Corresponding Source conveyed under this section must be +accompanied by the Installation Information. But this requirement does not apply if +neither you nor any third party retains the ability to install modified object code +on the User Product (for example, the work has been installed in ROM). + +The requirement to provide Installation Information does not include a requirement to +continue to provide support service, warranty, or updates for a work that has been +modified or installed by the recipient, or for the User Product in which it has been +modified or installed. Access to a network may be denied when the modification itself +materially and adversely affects the operation of the network or violates the rules +and protocols for communication across the network. + +Corresponding Source conveyed, and Installation Information provided, in accord with +this section must be in a format that is publicly documented (and with an +implementation available to the public in source code form), and must require no +special password or key for unpacking, reading or copying. + +### 7. Additional Terms + +“Additional permissions” are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. Additional +permissions that are applicable to the entire Program shall be treated as though they +were included in this License, to the extent that they are valid under applicable +law. If additional permissions apply only to part of the Program, that part may be +used separately under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + +When you convey a copy of a covered work, you may at your option remove any +additional permissions from that copy, or from any part of it. (Additional +permissions may be written to require their own removal in certain cases when you +modify the work.) You may place additional permissions on material, added by you to a +covered work, for which you have or can give appropriate copyright permission. + +Notwithstanding any other provision of this License, for material you add to a +covered work, you may (if authorized by the copyright holders of that material) +supplement the terms of this License with terms: + +* **a)** Disclaiming warranty or limiting liability differently from the terms of +sections 15 and 16 of this License; or +* **b)** Requiring preservation of specified reasonable legal notices or author +attributions in that material or in the Appropriate Legal Notices displayed by works +containing it; or +* **c)** Prohibiting misrepresentation of the origin of that material, or requiring that +modified versions of such material be marked in reasonable ways as different from the +original version; or +* **d)** Limiting the use for publicity purposes of names of licensors or authors of the +material; or +* **e)** Declining to grant rights under trademark law for use of some trade names, +trademarks, or service marks; or +* **f)** Requiring indemnification of licensors and authors of that material by anyone +who conveys the material (or modified versions of it) with contractual assumptions of +liability to the recipient, for any liability that these contractual assumptions +directly impose on those licensors and authors. + +All other non-permissive additional terms are considered “further +restrictions” within the meaning of section 10. If the Program as you received +it, or any part of it, contains a notice stating that it is governed by this License +along with a term that is a further restriction, you may remove that term. If a +license document contains a further restriction but permits relicensing or conveying +under this License, you may add to a covered work material governed by the terms of +that license document, provided that the further restriction does not survive such +relicensing or conveying. + +If you add terms to a covered work in accord with this section, you must place, in +the relevant source files, a statement of the additional terms that apply to those +files, or a notice indicating where to find the applicable terms. + +Additional terms, permissive or non-permissive, may be stated in the form of a +separately written license, or stated as exceptions; the above requirements apply +either way. + +### 8. Termination + +You may not propagate or modify a covered work except as expressly provided under +this License. Any attempt otherwise to propagate or modify it is void, and will +automatically terminate your rights under this License (including any patent licenses +granted under the third paragraph of section 11). + +However, if you cease all violation of this License, then your license from a +particular copyright holder is reinstated **(a)** provisionally, unless and until the +copyright holder explicitly and finally terminates your license, and **(b)** permanently, +if the copyright holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + +Moreover, your license from a particular copyright holder is reinstated permanently +if the copyright holder notifies you of the violation by some reasonable means, this +is the first time you have received notice of violation of this License (for any +work) from that copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + +Termination of your rights under this section does not terminate the licenses of +parties who have received copies or rights from you under this License. If your +rights have been terminated and not permanently reinstated, you do not qualify to +receive new licenses for the same material under section 10. + +### 9. Acceptance Not Required for Having Copies + +You are not required to accept this License in order to receive or run a copy of the +Program. Ancillary propagation of a covered work occurring solely as a consequence of +using peer-to-peer transmission to receive a copy likewise does not require +acceptance. However, nothing other than this License grants you permission to +propagate or modify any covered work. These actions infringe copyright if you do not +accept this License. Therefore, by modifying or propagating a covered work, you +indicate your acceptance of this License to do so. + +### 10. Automatic Licensing of Downstream Recipients + +Each time you convey a covered work, the recipient automatically receives a license +from the original licensors, to run, modify and propagate that work, subject to this +License. You are not responsible for enforcing compliance by third parties with this +License. + +An “entity transaction” is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an organization, or +merging organizations. If propagation of a covered work results from an entity +transaction, each party to that transaction who receives a copy of the work also +receives whatever licenses to the work the party's predecessor in interest had or +could give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if the predecessor +has it or can get it with reasonable efforts. + +You may not impose any further restrictions on the exercise of the rights granted or +affirmed under this License. For example, you may not impose a license fee, royalty, +or other charge for exercise of rights granted under this License, and you may not +initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging +that any patent claim is infringed by making, using, selling, offering for sale, or +importing the Program or any portion of it. + +### 11. Patents + +A “contributor” is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The work thus +licensed is called the contributor's “contributor version”. + +A contributor's “essential patent claims” are all patent claims owned or +controlled by the contributor, whether already acquired or hereafter acquired, that +would be infringed by some manner, permitted by this License, of making, using, or +selling its contributor version, but do not include claims that would be infringed +only as a consequence of further modification of the contributor version. For +purposes of this definition, “control” includes the right to grant patent +sublicenses in a manner consistent with the requirements of this License. + +Each contributor grants you a non-exclusive, worldwide, royalty-free patent license +under the contributor's essential patent claims, to make, use, sell, offer for sale, +import and otherwise run, modify and propagate the contents of its contributor +version. + +In the following three paragraphs, a “patent license” is any express +agreement or commitment, however denominated, not to enforce a patent (such as an +express permission to practice a patent or covenant not to sue for patent +infringement). To “grant” such a patent license to a party means to make +such an agreement or commitment not to enforce a patent against the party. + +If you convey a covered work, knowingly relying on a patent license, and the +Corresponding Source of the work is not available for anyone to copy, free of charge +and under the terms of this License, through a publicly available network server or +other readily accessible means, then you must either **(1)** cause the Corresponding +Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the +patent license for this particular work, or **(3)** arrange, in a manner consistent with +the requirements of this License, to extend the patent license to downstream +recipients. “Knowingly relying” means you have actual knowledge that, but +for the patent license, your conveying the covered work in a country, or your +recipient's use of the covered work in a country, would infringe one or more +identifiable patents in that country that you have reason to believe are valid. + +If, pursuant to or in connection with a single transaction or arrangement, you +convey, or propagate by procuring conveyance of, a covered work, and grant a patent +license to some of the parties receiving the covered work authorizing them to use, +propagate, modify or convey a specific copy of the covered work, then the patent +license you grant is automatically extended to all recipients of the covered work and +works based on it. + +A patent license is “discriminatory” if it does not include within the +scope of its coverage, prohibits the exercise of, or is conditioned on the +non-exercise of one or more of the rights that are specifically granted under this +License. You may not convey a covered work if you are a party to an arrangement with +a third party that is in the business of distributing software, under which you make +payment to the third party based on the extent of your activity of conveying the +work, and under which the third party grants, to any of the parties who would receive +the covered work from you, a discriminatory patent license **(a)** in connection with +copies of the covered work conveyed by you (or copies made from those copies), or **(b)** +primarily for and in connection with specific products or compilations that contain +the covered work, unless you entered into that arrangement, or that patent license +was granted, prior to 28 March 2007. + +Nothing in this License shall be construed as excluding or limiting any implied +license or other defenses to infringement that may otherwise be available to you +under applicable patent law. + +### 12. No Surrender of Others' Freedom + +If conditions are imposed on you (whether by court order, agreement or otherwise) +that contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot convey a covered work so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not convey it at all. For example, if you +agree to terms that obligate you to collect a royalty for further conveying from +those to whom you convey the Program, the only way you could satisfy both those terms +and this License would be to refrain entirely from conveying the Program. + +### 13. Use with the GNU Affero General Public License + +Notwithstanding any other provision of this License, you have permission to link or +combine any covered work with a work licensed under version 3 of the GNU Affero +General Public License into a single combined work, and to convey the resulting work. +The terms of this License will continue to apply to the part which is the covered +work, but the special requirements of the GNU Affero General Public License, section +13, concerning interaction through a network will apply to the combination as such. + +### 14. Revised Versions of this License + +The Free Software Foundation may publish revised and/or new versions of the GNU +General Public License from time to time. Such new versions will be similar in spirit +to the present version, but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Program specifies that +a certain numbered version of the GNU General Public License “or any later +version” applies to it, you have the option of following the terms and +conditions either of that numbered version or of any later version published by the +Free Software Foundation. If the Program does not specify a version number of the GNU +General Public License, you may choose any version ever published by the Free +Software Foundation. + +If the Program specifies that a proxy can decide which future versions of the GNU +General Public License can be used, that proxy's public statement of acceptance of a +version permanently authorizes you to choose that version for the Program. + +Later license versions may give you additional or different permissions. However, no +additional obligations are imposed on any author or copyright holder as a result of +your choosing to follow a later version. + +### 15. Disclaimer of Warranty + +THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE +QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE +DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +### 16. Limitation of Liability + +IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY +COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS +PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, +INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE +OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE +WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + +### 17. Interpretation of Sections 15 and 16 + +If the disclaimer of warranty and limitation of liability provided above cannot be +given local legal effect according to their terms, reviewing courts shall apply local +law that most closely approximates an absolute waiver of all civil liability in +connection with the Program, unless a warranty or assumption of liability accompanies +a copy of the Program in return for a fee. + +_END OF TERMS AND CONDITIONS_ + +## How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible use to +the public, the best way to achieve this is to make it free software which everyone +can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach them +to the start of each source file to most effectively state the exclusion of warranty; +and each file should have at least the “copyright” line and a pointer to +where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If the program does terminal interaction, make it output a short notice like this +when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type 'show c' for details. + +The hypothetical commands `show w` and `show c` should show the appropriate parts of +the General Public License. Of course, your program's commands might be different; +for a GUI interface, you would use an “about box”. + +You should also get your employer (if you work as a programmer) or school, if any, to +sign a “copyright disclaimer” for the program, if necessary. For more +information on this, and how to apply and follow the GNU GPL, see +<>. + +The GNU General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may consider it +more useful to permit linking proprietary applications with the library. If this is +what you want to do, use the GNU Lesser General Public License instead of this +License. But first, please read +<>. diff --git a/NAMESPACE b/NAMESPACE index f15ea8f..2c1db69 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -1,12 +1,6 @@ -useDynLib(RcppClock, .registration = TRUE) +# Generated by roxygen2: do not edit by hand +export(fibonacci) +export(fibonacci_omp) import(Rcpp) -import(ggplot2) -importFrom(stats,aggregate) -importFrom(stats,sd) - -S3method(plot,RcppClock) -S3method(print,RcppClock) -S3method(summary,RcppClock) - -export(fibonacci) \ No newline at end of file +useDynLib(rcpptimer, .registration = TRUE) diff --git a/NEWS.md b/NEWS.md new file mode 100644 index 0000000..867f0cd --- /dev/null +++ b/NEWS.md @@ -0,0 +1,10 @@ +rcpptimer 1.0.0 +============== + +This is the initial release of `rcpptimer`. It is based on `RcppClock` but contains a number of improvements: +- OpenMP support +- Auomatically returns results to R as soon as the C++ Object goes out of scope +- Fast computation of Mean and Standard Deviation of the results in C++ +- Uses `tic` and `toc` instead of `tick` and `tock` to be consistent with R's `tictoc` package +- Allways reports microseconds resolution +- Many more performance improvements diff --git a/R/RcppClock.R b/R/RcppClock.R deleted file mode 100644 index 2c45479..0000000 --- a/R/RcppClock.R +++ /dev/null @@ -1,158 +0,0 @@ -#' RcppClock -#' -#' Time Rcpp functions and summarize, print, and plot runtime statistics -#' -#' @rdname RcppClock -#' -#' @details -#' See https://github.com/zdebruine/RcppClock/readme.md for information on how to use the package. -#' -#' @section RcppClock functions: -#' See the RcppClock README on \code{https://github.com/zdebruine/RcppClock#readme} for basic usage examples. -#' -#' When the Rcpp `Rcpp::clock::stop()` method is called in Rcpp code, an S3 \code{RcppClock} object -#' will be created in the global environment. This object contains three methods: -#' -#' * \code{summary}: computes runtime summary statistics and returns a \code{data.frame} -#' * \code{print}: runs \code{summary} and then prints the resulting \code{data.frame} -#' * \code{plot}: a ggplot2 violin plot with jitter points showing runtimes for each expression -#' -#' The \code{\link{fibonacci}} function is a simple example of how to use RcppClock. -#' See the source code on \code{github.com/zdebruine/RcppClock/src/fibonacci.cpp} -#' -#' @docType package -#' @name RcppClock -#' @useDynLib RcppClock -#' @importFrom stats aggregate sd -#' @import ggplot2 -#' @aliases RcppClock, RcppClock-package, RcppClock-class -#' @seealso \code{\link{fibonacci}} -#' @md -#' @examples -#' library(RcppClock) -#' fibonacci(n = 25:35, reps = 10) -#' # this function creates a global environment variable "clock" -#' # that is an S3 RcppClock object -#' clock -#' plot(clock) -#' summary(clock, units = "ms") -#' -#' \dontrun{ -#' # this is the Rcpp code behind the "fibonacci" example function -#' -#' ```{Rcpp} -#' //[[Rcpp::depends(RcppClock)]] -#' #include -#' -#' int fib(int n) { -#' return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); -#' } -#' -#' //[[Rcpp::export]] -#' void fibonacci(std::vector n, int reps = 10) { -#' Rcpp::Clock clock; -#' while(reps-- > 0){ -#' for(auto number : n){ -#' clock.tick("fib" + std::to_string(number)); -#' fib(number); -#' clock.tock("fib" + std::to_string(number)); -#' } -#' } -#' clock.stop("clock"); -#' } -#' ``` -#' } -NULL - -#' @rdname RcppClock -#' @param x RcppClock object -#' @param object RcppClock object -#' @param units nanoseconds (\code{"ns"}), microseconds (\code{"us"}), milliseconds (\code{"ms"}), seconds (\code{"s"}), or auto (\code{"auto"}) -#' @param ... arguments to other functions -#' @export -#' -summary.RcppClock <- function(object, units = "auto", ...){ - min_time <- min(object$timer[object$timer != 0]) - if(is.na(min_time)) min_time <- 0 - if(units == "auto"){ - if(min_time > 1e8){ - units <- "s" - } else if(min_time > 1e5){ - units <- "ms" - } else if(min_time > 1e2){ - units <- "us" - } else { - units <- "ns" - } - } - if(units == "s"){ - object$timer <- object$timer / 1e9 - } else if (units == "ms") { - object$timer <- object$timer / 1e6 - } else if (units == "us") { - object$timer <- object$timer / 1e3 - } - - object$ticker <- factor(object$ticker, levels = unique(object$ticker)) - - # summarize results - object <- data.frame("timer" = object$timer, "ticker" = object$ticker) - df2 <- aggregate(object$timer, list(ticker = object$ticker), mean) - colnames(df2)[2] <- "mean" - df2$sd <- aggregate(object$timer, list(ticker = object$ticker), sd)$x - df2$min <- aggregate(object$timer, list(ticker = object$ticker), min)$x - df2$max <- aggregate(object$timer, list(ticker = object$ticker), max)$x - object$timer <- 1 - df2$neval <- aggregate(object$timer, list(ticker = object$ticker), sum)$x - - long_units <- c("seconds", "milliseconds", "microseconds", "nanoseconds") - short_units <- c("s", "ms", "us", "ns") - attr(df2, "units") <- long_units[which(short_units == units)] - df2 -} - -#' @method print RcppClock -#' @rdname RcppClock -#' @export -print.RcppClock <- function(x, ...){ - df <- summary(x, units = "auto") - cat("Unit:", attr(df, "units"), "\n") - print(df, digits = 4, row.names = FALSE) - invisible(x) -} - -#' @export -#' @method plot RcppClock -#' @rdname RcppClock -plot.RcppClock <- function(x, ...) { - min_time <- min(x$timer[x$timer != 0]) - if(is.na(min_time)) min_time <- 0 - if(min_time > 1e8) { - units <- "s" - x$timer <- x$timer / 1e9 - } else if(min_time > 1e7) { - units <- "ms" - x$timer <- x$timer / 1e6 - } else if(min_time > 1e2) { - units <- "us" - x$timer <- x$timer / 1e3 - } else { - units <- "ns" - } - - tickers <- unique(x$ticker) - - long_units <- c("seconds", "milliseconds", "microseconds", "nanoseconds") - short_units <- c("s", "ms", "us", "ns") - - df <- data.frame("timer" = x$timer, "ticker" = x$ticker) - df$ticker <- factor(df$ticker, levels = rev(unique(df$ticker))) - - suppressWarnings(print(ggplot(df, aes_string(y = "ticker", x = "timer")) + - geom_violin(scale = "width") + - geom_jitter(height = 0.1, size = 0.8) + - theme_classic() + - scale_x_continuous(trans = "log10") + - theme(axis.text.x = element_text(angle = 45, hjust = 1)) + - labs(y = "", x = paste0("runtime (", long_units[which(short_units == units)], ")")))) -} diff --git a/R/RcppExports.R b/R/RcppExports.R index ae0e8f7..baf0b7b 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -1,28 +1,53 @@ # Generated by using Rcpp::compileAttributes() -> do not edit by hand # Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -#' Simple RcppClock example -#' -#' Time the computation of fibonacci numbers -#' +#' Simple rcpptimer example +#' +#' Time the computation of Fibonacci numbers +#' +#' @details +#' The function being timed is the following: +#' +#' \code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} +#' +#' Runtime for computations less than \code{n = 15} is nearly unmeasurable. +#' +#' @param n vector giving integers for which to compute the Fibonacci sum +#' @return vector of integers giving the Fibonacci sum for each element in +#' \code{n} +#' @export +#' @examples +#' \donttest{ +#' fibonacci(n = rep(10*(1:4), 10)) +#' # this function creates a global environment variable "times" +#' times +#' } +fibonacci <- function(n) { + .Call(`_rcpptimer_fibonacci`, n) +} + +#' Simple rcpptimer example using OpenMP +#' +#' Time the multithreaded computation of Fibonacci numbers +#' #' @details #' The function being timed is the following: #' #' \code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} -#' -#' Runtime for computations less than \code{n = 25} is nearly unmeasurable. -#' -#' @param n vector giving integers for which to compute the fibonacci sum -#' @param reps number of replicates for timing +#' +#' Runtime for computations less than \code{n = 15} is nearly unmeasurable. +#' +#' @param n vector giving integers for which to compute the Fibonacci sum +#' @return vector of integers giving the Fibonacci sum for each element in +#' \code{n} #' @export #' @examples -#' fibonacci(n = c(25:35), reps = 10) -#' # this function creates a global environment variable "clock" -#' # that is an S3 RcppClock object -#' clock -#' plot(clock) -#' summary(clock, units = "ms") -fibonacci <- function(n, reps = 10L) { - invisible(.Call(`_RcppClock_fibonacci`, n, reps)) +#' \donttest{ +#' fibonacci_omp(n = rep(10*(1:4), 10)) +#' # this function creates a global environment variable "times" +#' times +#' } +fibonacci_omp <- function(n) { + .Call(`_rcpptimer_fibonacci_omp`, n) } diff --git a/R/package.R b/R/package.R new file mode 100644 index 0000000..212e768 --- /dev/null +++ b/R/package.R @@ -0,0 +1,5 @@ +## usethis namespace: start +#' @useDynLib rcpptimer, .registration = TRUE +#' @import Rcpp +## usethis namespace: end +NULL diff --git a/README.md b/README.md index a56e537..6c85862 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,102 @@ -# RcppClock +# RcppTimeR - Rcpp Tic-Toc Timer with OpenMP Support -[![](https://cranlogs.r-pkg.org/badges/grand-total/RcppClock)](https://cran.r-project.org/package=RcppML) -[![](https://www.r-pkg.org/badges/version-last-release/RcppClock)](https://cran.r-project.org/package=RcppML) -[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-blue.svg)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) +This R Package provides Rcpp bindings for [cpptimer](https://github.com/BerriJ/cpptimer), a simple tic-toc timer class for benchmarking C++ code. It's not just simple, it's blazing fast! This sleek tic-toc timer class supports overlapping timers as well as OpenMP parallelism. It boasts a microsecond-level time resolution. We did not find any overhead of the timer itself at this resolution. Results (with summary statistics) are automatically passed back to R as a data frame. -RcppClock is a simple wrapper for `std::chrono::high_resolution_clock` that makes benchmarking Rcpp code easy. -Install RcppClock from CRAN. +## Install -``` -install.packages("RcppClock") -library(RcppClock) -?RcppClock -``` - -## The Rcpp side of things - -Load the RcppClock header into your R session using `library(RcppClock)`, link it in your `DESCRIPTION` file or with `//[[Rcpp::depends(RcppClock)]]`, and load the header library into individual `.cpp` files with `#include `: +Install rcpptimer from CRAN. ``` -//[[Rcpp::depends(RcppClock)]] -#include -#include - -//[[Rcpp::export]] -void sleepy(){ - Rcpp::Clock clock; - - clock.tick("both_naps"); - - clock.tick("short_nap"); - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - clock.tock("short_nap"); - - clock.tick("long_nap"); - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - clock.tock("long_nap"); - - clock.tock("both_naps"); - - // send the times to the R global environment variable, named "naptimes" - clock.stop("naptimes"); -} +install.packages("rcpptimer") ``` -`.tick(std::string)` starts a new timer. Provide a name to record what is being timed. +## The Rcpp side of things -`.tock(std::string)` stops a timer. It is important to use the same name as declared in `.tick()`. +Link it in your `DESCRIPTION` file or with `//[[Rcpp::depends(rcpptimer)]]`, and load the header library into individual `.cpp` files with `#include `. Then create an instance of the Rcpp::Clock class and use: -`.stop(std::string)` calculates the duration between all `.tick()` and `.tock()` timing results, and creates an object in the R environment with the name provided. +`.tic(std::string)` to start a new timer. `.toc(std::string)` to stop the timer. -## The R side of things +```c++ +//[[Rcpp::depends(rcpptimer)]] +#include -On the R end, we can now do stuff with the "naptimes" variable that was created in the above Rcpp function: +std::vector fibonacci(std::vector n) +{ + Rcpp::Timer timer; // Or Rcpp::Timer timer("my_name"); to assign a custom name + // to the returned dataframe (default is 'times') + timer.tic("fib_body"); // Start timer measuring the whole function + std::vector results = n; -```{R} -sleepy() -# global variable "naptimes" is now created in the environment -naptimes + for (int i = 0; i < n.size(); ++i) + { + // Start a timer for each fibonacci number + timer.tic("fib_" + std::to_string(n[i])); + results[i] = fib(n[i]); + // Stop the timer for each fibonacci number + timer.toc("fib_" + std::to_string(n[i])); + } + // Stop the timer measuring the whole function + timer.toc("fib_body"); + return (results); +} ``` +Multiple timers with the same name (i.e. in a loop) will be grouped and we report the Mean and Standard Deviation for them. The results will be automatically passed to R as the `timer` instance goes out of scope. You don't need to worry about return statements. -```{R} -summary(naptimes, units = "us") -``` +## The R side of things -```{R} -plot(naptimes) +On the R end, we can now observe the `times` object that was silently passed to the global environment: + +```r +[R] fibonacci(n = rep(10 * (1:4), 10)) +[R] times + Name Milliseconds SD Count +1 fib_10 0.002 0.001 10 +2 fib_20 0.048 0.011 10 +3 fib_30 5.382 0.070 10 +4 fib_40 658.280 1.520 10 +5 fib_body 6637.259 0.000 1 ``` -## Timing multiple replicates +## OpenMP Support -If a `.tick()` with the same name is called multiple times, RcppClock automatically groups the results. +Since we added OpenMP support, we also have an OpenMP version of the `fibonacci` function: -The following code reproduces the `?fibonacci` function example included in the RcppClock package: +```c++ +std::vector fibonacci_omp(std::vector n) +{ -``` -int fib(int n) { - return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); -} + Rcpp::Timer timer; + timer.tic("fib_body"); + std::vector results = n; -//[[Rcpp::export]] -void fibonacci(std::vector n, int reps = 10) { - Rcpp::Clock clock; - - while(reps-- > 0){ - for(auto number : n){ - clock.tick("fib" + std::to_string(number)); - fib(number); - clock.tock("fib" + std::to_string(number)); - } +#pragma omp parallel for + for (int i = 0; i < n.size(); ++i) + { + timer.tic("fib_" + std::to_string(n[i])); + results[i] = fib(n[i]); + timer.toc("fib_" + std::to_string(n[i])); } - clock.stop("clock"); + timer.toc("fib_body"); + return (results); } ``` -On the R end, we'll get an object named "clock": +Nothing has to be changed with respect to your `timer` instance. The timings show that the OpenMP version is significantly faster (fib_body): -```{R} -fibonacci(n = 25:35, reps = 10) -# global variable "clock" is created in the R global environment -clock -``` - -```{R} -plot(clock) +```r + Name Milliseconds SD Count +1 fib_10 0.022 0.031 10 +2 fib_20 0.132 0.057 10 +3 fib_30 8.728 2.583 10 +4 fib_40 779.942 91.569 10 +5 fib_body 908.919 0.000 1 ``` ## Limitations -* Not compatible with OpenMP parallelization. -* Processes taking less than a microsecond cannot be reliably timed on some platforms. +Processes taking less than a microsecond cannot be timed. + +## Acknowledgments + +This package (and the underlying [cpptimer](https://github.com/BerriJ/cpptimer) class) was inspired by [zdebruine](https://github.com/zdebruine)'s [RcppClock](https://github.com/zdebruine/RcppClock). I used that package a lot and wanted to add OpenMP support, alter the process of calculating summary statistics, and apply a series of other small adjustments. I hope you find it useful. \ No newline at end of file diff --git a/_pkgdown.yml b/_pkgdown.yml new file mode 100644 index 0000000..8564e74 --- /dev/null +++ b/_pkgdown.yml @@ -0,0 +1,6 @@ +url: https://rcpptimer.berrisch.biz +template: + bootstrap: 5 + +development: + mode: auto \ No newline at end of file diff --git a/cran-comments.md b/cran-comments.md new file mode 100644 index 0000000..858617d --- /dev/null +++ b/cran-comments.md @@ -0,0 +1,5 @@ +## R CMD check results + +0 errors | 0 warnings | 1 note + +* This is a new release. diff --git a/inst/include/RcppClock.h b/inst/include/RcppClock.h deleted file mode 100644 index 39de8a1..0000000 --- a/inst/include/RcppClock.h +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include - -#define duration(a) std::chrono::duration_cast(a).count() -#define now() std::chrono::high_resolution_clock::now() - -typedef std::map, std::chrono::high_resolution_clock::time_point> TimesMap; - -namespace Rcpp -{ - class Clock - { - private: - std::vector timers; - size_t ticks, tocks = 0; - TimesMap tickmap, tockmap; - std::pair key; - - public: - // start a timer - void tick(std::string name) - { - key.first = name; - key.second = ticks; - tickmap.insert( - std::pair, std::chrono::high_resolution_clock::time_point>(key, now())); - ticks += 1; - } - - // stop a timer - void tock(std::string name) - { - key.first = name; - key.second = tocks; - tockmap.insert( - std::pair, std::chrono::high_resolution_clock::time_point>(key, now())); - tocks += 1; - } - - // calculate timer durations - void stop(std::string var_name) - { - std::vector keys; - keys.reserve(tickmap.size()); - std::vector ticks; - ticks.reserve(tickmap.size()); - for (auto kv : tickmap) - { - keys.push_back(kv.first.first); - ticks.push_back(kv.second); - } - - std::vector tocks; - tocks.reserve(tockmap.size()); - for (auto kv : tockmap) - { - tocks.push_back(kv.second); - } - - for (std::size_t i = 0; i < ticks.size(); ++i) - { - timers.push_back(duration(tocks[i] - ticks[i])); - } - DataFrame df = DataFrame::create(Named("ticker") = keys, Named("timer") = timers); - df.attr("class") = "RcppClock"; - Environment env = Environment::global_env(); - env[var_name] = df; - } - }; -} diff --git a/inst/include/cpptimer b/inst/include/cpptimer new file mode 160000 index 0000000..1be359a --- /dev/null +++ b/inst/include/cpptimer @@ -0,0 +1 @@ +Subproject commit 1be359ab5bae5c90ffef1aef0438c342e15d0890 diff --git a/inst/include/rcpptimer.h b/inst/include/rcpptimer.h new file mode 100644 index 0000000..2ff18af --- /dev/null +++ b/inst/include/rcpptimer.h @@ -0,0 +1,67 @@ +#ifndef rcpptimer +#define rcpptimer + +#ifdef _OPENMP +#include +#endif + +#include +#include +#include +#include + +namespace Rcpp +{ + // This class inherits its main functionality from CppClock + // It justs extends it with a stop method that passes the data to R and + // a destructor that calls stop for convenience. + class Timer : public CppTimer + { + + public: + bool autoreturn = true; + + // Pass data to R / Python + void stop() + { + aggregate(); + + // Output Objects + std::vector out_tags; + std::vector out_counts; + std::vector out_means, out_sd; + + for (auto const &ent1 : data) + { + // Save tag + out_tags.push_back(ent1.first); + + unsigned long int count = std::get<2>(ent1.second); + double mean = std::get<0>(ent1.second); + double variance = std::get<1>(ent1.second) / count; + + // Convert to milliseconds and round to 3 decimal places + out_means.push_back(std::round(mean) * 1e-3); + out_sd.push_back(std::round(std::sqrt(variance * 1e-6) * 1e+3) * 1e-3); + out_counts.push_back(count); + } + + DataFrame df = DataFrame::create( + Named("Name") = out_tags, + Named("Milliseconds") = out_means, + Named("SD") = out_sd, + Named("Count") = out_counts); + + Environment env = Environment::global_env(); + env[name] = df; + } + + // Destructor + ~Timer() + { + if (autoreturn) + stop(); + } + }; +} +#endif \ No newline at end of file diff --git a/man/RcppClock.Rd b/man/RcppClock.Rd deleted file mode 100644 index b2348d0..0000000 --- a/man/RcppClock.Rd +++ /dev/null @@ -1,88 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/RcppClock.R -\docType{package} -\name{RcppClock} -\alias{RcppClock} -\alias{RcppClock,} -\alias{RcppClock-package,} -\alias{RcppClock-class} -\alias{summary.RcppClock} -\alias{print.RcppClock} -\alias{plot.RcppClock} -\title{RcppClock} -\usage{ -\method{summary}{RcppClock}(object, units = "auto", ...) - -\method{print}{RcppClock}(x, ...) - -\method{plot}{RcppClock}(x, ...) -} -\arguments{ -\item{object}{RcppClock object} - -\item{units}{nanoseconds (\code{"ns"}), microseconds (\code{"us"}), milliseconds (\code{"ms"}), seconds (\code{"s"}), or auto (\code{"auto"})} - -\item{...}{arguments to other functions} - -\item{x}{RcppClock object} -} -\description{ -Time Rcpp functions and summarize, print, and plot runtime statistics -} -\details{ -See https://github.com/zdebruine/RcppClock/readme.md for information on how to use the package. -} -\section{RcppClock functions}{ - -See the RcppClock README on \code{https://github.com/zdebruine/RcppClock#readme} for basic usage examples. - -When the Rcpp \verb{Rcpp::clock::stop()} method is called in Rcpp code, an S3 \code{RcppClock} object -will be created in the global environment. This object contains three methods: -\itemize{ -\item \code{summary}: computes runtime summary statistics and returns a \code{data.frame} -\item \code{print}: runs \code{summary} and then prints the resulting \code{data.frame} -\item \code{plot}: a ggplot2 violin plot with jitter points showing runtimes for each expression -} - -The \code{\link{fibonacci}} function is a simple example of how to use RcppClock. -See the source code on \code{github.com/zdebruine/RcppClock/src/fibonacci.cpp} -} - -\examples{ -library(RcppClock) -fibonacci(n = 25:35, reps = 10) -# this function creates a global environment variable "clock" -# that is an S3 RcppClock object -clock -plot(clock) -summary(clock, units = "ms") - -\dontrun{ -# this is the Rcpp code behind the "fibonacci" example function - -```{Rcpp} -//[[Rcpp::depends(RcppClock)]] -#include - -int fib(int n) { -return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); -} - -//[[Rcpp::export]] -void fibonacci(std::vector n, int reps = 10) { - Rcpp::Clock clock; - while(reps-- > 0){ - for(auto number : n){ - clock.tick("fib" + std::to_string(number)); - fib(number); - clock.tock("fib" + std::to_string(number)); - } - } - clock.stop("clock"); -} -``` -} -} -\seealso{ -\code{\link{fibonacci}} -} diff --git a/man/fibonacci.Rd b/man/fibonacci.Rd index 10ad80b..ade8eae 100644 --- a/man/fibonacci.Rd +++ b/man/fibonacci.Rd @@ -2,30 +2,31 @@ % Please edit documentation in R/RcppExports.R \name{fibonacci} \alias{fibonacci} -\title{Simple RcppClock example} +\title{Simple rcpptimer example} \usage{ -fibonacci(n, reps = 10L) +fibonacci(n) } \arguments{ -\item{n}{vector giving integers for which to compute the fibonacci sum} - -\item{reps}{number of replicates for timing} +\item{n}{vector giving integers for which to compute the Fibonacci sum} +} +\value{ +vector of integers giving the Fibonacci sum for each element in +\code{n} } \description{ -Time the computation of fibonacci numbers +Time the computation of Fibonacci numbers } \details{ The function being timed is the following: \code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} -Runtime for computations less than \code{n = 25} is nearly unmeasurable. +Runtime for computations less than \code{n = 15} is nearly unmeasurable. } \examples{ -fibonacci(n = c(25:35), reps = 10) -# this function creates a global environment variable "clock" -# that is an S3 RcppClock object -clock -plot(clock) -summary(clock, units = "ms") +\donttest{ +fibonacci(n = rep(10*(1:4), 10)) +# this function creates a global environment variable "times" +times +} } diff --git a/man/fibonacci_omp.Rd b/man/fibonacci_omp.Rd new file mode 100644 index 0000000..72383a3 --- /dev/null +++ b/man/fibonacci_omp.Rd @@ -0,0 +1,32 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/RcppExports.R +\name{fibonacci_omp} +\alias{fibonacci_omp} +\title{Simple rcpptimer example using OpenMP} +\usage{ +fibonacci_omp(n) +} +\arguments{ +\item{n}{vector giving integers for which to compute the Fibonacci sum} +} +\value{ +vector of integers giving the Fibonacci sum for each element in +\code{n} +} +\description{ +Time the multithreaded computation of Fibonacci numbers +} +\details{ +The function being timed is the following: + +\code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} + +Runtime for computations less than \code{n = 15} is nearly unmeasurable. +} +\examples{ +\donttest{ +fibonacci_omp(n = rep(10*(1:4), 10)) +# this function creates a global environment variable "times" +times +} +} diff --git a/src/Makevars b/src/Makevars index 448d604..d2fb02a 100644 --- a/src/Makevars +++ b/src/Makevars @@ -1,3 +1,5 @@ -PKG_CPPFLAGS = -I../inst/include/ -PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -CXX_STD = CXX11 \ No newline at end of file +INC = -I../inst/include + +PKG_CXXFLAGS = $(INC) $(SHLIB_OPENMP_CXXFLAGS) + +PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) diff --git a/src/Makevars.win b/src/Makevars.win index 448d604..b8ff26e 100644 --- a/src/Makevars.win +++ b/src/Makevars.win @@ -1,3 +1,19 @@ -PKG_CPPFLAGS = -I../inst/include/ -PKG_LIBS = $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) -CXX_STD = CXX11 \ No newline at end of file + +## With R 3.1.0 or later, you can uncomment the following line to tell R to +## enable compilation with C++11 (where available) +## +## Also, OpenMP support in Armadillo prefers C++11 support. However, for wider +## availability of the package we do not yet enforce this here. It is however +## recommended for client packages to set it. +## +## And with R 3.4.0, and RcppArmadillo 0.7.960.*, we turn C++11 on as OpenMP +## support within Armadillo prefers / requires it + +# Paralelization (must be set locally since CRAN does not like that) +# MAKEFLAGS = -j12 + +INC = -I../inst/include + +PKG_CXXFLAGS = $(INC) $(SHLIB_OPENMP_CXXFLAGS) + +PKG_LIBS = $(SHLIB_OPENMP_CXXFLAGS) $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) \ No newline at end of file diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 4c2b6d9..f03ae46 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -1,7 +1,7 @@ // Generated by using Rcpp::compileAttributes() -> do not edit by hand // Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393 -#include "../inst/include/RcppClock.h" +#include "../inst/include/rcpptimer.h" #include using namespace Rcpp; @@ -12,23 +12,35 @@ Rcpp::Rostream& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get(); #endif // fibonacci -void fibonacci(std::vector n, int reps); -RcppExport SEXP _RcppClock_fibonacci(SEXP nSEXP, SEXP repsSEXP) { +std::vector fibonacci(std::vector n); +RcppExport SEXP _rcpptimer_fibonacci(SEXP nSEXP) { BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; Rcpp::RNGScope rcpp_rngScope_gen; Rcpp::traits::input_parameter< std::vector >::type n(nSEXP); - Rcpp::traits::input_parameter< int >::type reps(repsSEXP); - fibonacci(n, reps); - return R_NilValue; + rcpp_result_gen = Rcpp::wrap(fibonacci(n)); + return rcpp_result_gen; +END_RCPP +} +// fibonacci_omp +std::vector fibonacci_omp(std::vector n); +RcppExport SEXP _rcpptimer_fibonacci_omp(SEXP nSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< std::vector >::type n(nSEXP); + rcpp_result_gen = Rcpp::wrap(fibonacci_omp(n)); + return rcpp_result_gen; END_RCPP } static const R_CallMethodDef CallEntries[] = { - {"_RcppClock_fibonacci", (DL_FUNC) &_RcppClock_fibonacci, 2}, + {"_rcpptimer_fibonacci", (DL_FUNC) &_rcpptimer_fibonacci, 1}, + {"_rcpptimer_fibonacci_omp", (DL_FUNC) &_rcpptimer_fibonacci_omp, 1}, {NULL, NULL, 0} }; -RcppExport void R_init_RcppClock(DllInfo *dll) { +RcppExport void R_init_rcpptimer(DllInfo *dll) { R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); R_useDynamicSymbols(dll, FALSE); } diff --git a/src/fibonacci.cpp b/src/fibonacci.cpp index aa4bcfc..9948826 100644 --- a/src/fibonacci.cpp +++ b/src/fibonacci.cpp @@ -1,41 +1,86 @@ -#include +#include // a simple timing example -int fib(int n) { +int fib(int n) +{ return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); } -//' Simple RcppClock example -//' -//' Time the computation of fibonacci numbers -//' +//' Simple rcpptimer example +//' +//' Time the computation of Fibonacci numbers +//' //' @details //' The function being timed is the following: //' //' \code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} -//' -//' Runtime for computations less than \code{n = 25} is nearly unmeasurable. -//' -//' @param n vector giving integers for which to compute the fibonacci sum -//' @param reps number of replicates for timing +//' +//' Runtime for computations less than \code{n = 15} is nearly unmeasurable. +//' +//' @param n vector giving integers for which to compute the Fibonacci sum +//' @return vector of integers giving the Fibonacci sum for each element in +//' \code{n} //' @export //' @examples -//' fibonacci(n = c(25:35), reps = 10) -//' # this function creates a global environment variable "clock" -//' # that is an S3 RcppClock object -//' clock -//' plot(clock) -//' summary(clock, units = "ms") +//' \donttest{ +//' fibonacci(n = rep(10*(1:4), 10)) +//' # this function creates a global environment variable "times" +//' times +//' } //[[Rcpp::export]] -void fibonacci(std::vector n, int reps = 10) { - Rcpp::Clock clock; +std::vector fibonacci(std::vector n) +{ + + Rcpp::Timer timer; + timer.tic("fib_body"); + std::vector results = n; + + for (int i = 0; i < n.size(); ++i) + { + timer.tic("fib_" + std::to_string(n[i])); + results[i] = fib(n[i]); + timer.toc("fib_" + std::to_string(n[i])); + } + timer.toc("fib_body"); + return (results); +} + +//' Simple rcpptimer example using OpenMP +//' +//' Time the multithreaded computation of Fibonacci numbers +//' +//' @details +//' The function being timed is the following: +//' +//' \code{int fib(int n) { return ((n <= 1) ? n : fib(n - 1) + fib(n - 2)); }} +//' +//' Runtime for computations less than \code{n = 15} is nearly unmeasurable. +//' +//' @param n vector giving integers for which to compute the Fibonacci sum +//' @return vector of integers giving the Fibonacci sum for each element in +//' \code{n} +//' @export +//' @examples +//' \donttest{ +//' fibonacci_omp(n = rep(10*(1:4), 10)) +//' # this function creates a global environment variable "times" +//' times +//' } +//[[Rcpp::export]] +std::vector fibonacci_omp(std::vector n) +{ + + Rcpp::Timer timer; + timer.tic("fib_body"); + std::vector results = n; - for(int i = 0; i < reps; ++i){ - for(auto number : n){ - clock.tick("fib" + std::to_string(number)); - fib(number); - clock.tock("fib" + std::to_string(number)); - } +#pragma omp parallel for + for (int i = 0; i < n.size(); ++i) + { + timer.tic("fib_" + std::to_string(n[i])); + results[i] = fib(n[i]); + timer.toc("fib_" + std::to_string(n[i])); } - clock.stop("clock"); + timer.toc("fib_body"); + return (results); } \ No newline at end of file diff --git a/tests/testthat.R b/tests/testthat.R index 0815091..446e787 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,4 +1,6 @@ +Sys.setenv("OMP_THREAD_LIMIT" = 2) + library(testthat) -library(RcppClock) +library(rcpptimer) -test_check("RcppClock") +test_check("rcpptimer") diff --git a/tests/testthat/test_RcppClock.R b/tests/testthat/test_RcppClock.R deleted file mode 100644 index c309066..0000000 --- a/tests/testthat/test_RcppClock.R +++ /dev/null @@ -1,10 +0,0 @@ -test_that("Testing Rcpp::Clock", { - - fibonacci(n = c(25, 30, 35), reps = 10) - - s <- summary(clock) - expect_equal(all(colnames(s) == c("ticker", "mean", "sd", "min", "max", "neval")), TRUE) - expect_equal(all(s$neval == 10), TRUE) - expect_equal(all(!is.na(s)), TRUE) - -}) \ No newline at end of file diff --git a/tests/testthat/test_rcppclock.R b/tests/testthat/test_rcppclock.R new file mode 100644 index 0000000..8b2bc3d --- /dev/null +++ b/tests/testthat/test_rcppclock.R @@ -0,0 +1,17 @@ +fibonacci(n = rep(5 * (1:4), 3)) + +expect_false(is.null(times)) +expect_true(all(!is.na(times))) +expect_gte(min(times$Milliseconds), 0) +expect_gte(min(times$SD), 0) +expect_gt(min(times$Count), 0) + +times_sc <- times + +fibonacci_omp(n = rep(5 * (1:4), 3)) + +expect_false(is.null(times)) +expect_true(all(!is.na(times))) +expect_gte(min(times$Milliseconds), 0) +expect_gte(min(times$SD), 0) +expect_gt(min(times$Count), 0)