Skip to content

Commit

Permalink
Add 'gprofiler/' from commit '9d31be4531101a56ac8732a687016229a72029e6'
Browse files Browse the repository at this point in the history
git-subtree-dir: gprofiler
git-subtree-mainline: 6ffd7bf
git-subtree-split: 9d31be4
  • Loading branch information
krlmlr committed Dec 24, 2017
2 parents 6ffd7bf + 9d31be4 commit f42b2c7
Show file tree
Hide file tree
Showing 223 changed files with 44,210 additions and 0 deletions.
17 changes: 17 additions & 0 deletions gprofiler/.Rbuildignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
^.*\.Rproj$
^\.Rproj\.user$
^CMakeLists\.txt$
^cmake-build-debug$
^clion-test\.R$
^.*\.prof$
^.*\.prof\.out$
^README\.Rmd$
^README-.*\.png$
^\.travis\.yml$
^appveyor\.yml$
^tic\.R$
^src/pprof$
^src/go/src/github.com/google/pprof/internal/driver/testdata$
^src/go/src/github.com/google/pprof/\.
^src/go/src/\.idea$
^src/main\..*$
12 changes: 12 additions & 0 deletions gprofiler/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.Rproj.user
.Rhistory
.RData
/.idea
clion-test.R
cmake-build-debug
*.prof
*.prof.out
inst/doc
src/pprof
src/go/src/.idea/
src/main.*
63 changes: 63 additions & 0 deletions gprofiler/.travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# R for travis: see documentation at https://docs.travis-ci.com/user/languages/r
# Default configuration for use with tic package
# Usually you shouldn't need to change the first part of the file

# DO NOT CHANGE THE CODE BELOW
before_install: R -q -e 'install.packages("remotes"); remotes::install_github("ropenscilabs/tic"); tic::prepare_all_stages(); tic::before_install()'
install: R -q -e 'tic::install()'
after_install: R -q -e 'tic::after_install()'
before_script: R -q -e 'tic::before_script()'
script: R -q -e 'tic::script()'
after_success: R -q -e 'tic::after_success()'
after_failure: R -q -e 'tic::after_failure()'
before_deploy: R -q -e 'tic::before_deploy()'
deploy:
provider: script
script: R -q -e 'tic::deploy()'
on:
all_branches: true
after_deploy: R -q -e 'tic::after_deploy()'
after_script: R -q -e 'tic::after_script()'
# DO NOT CHANGE THE CODE ABOVE

# Custom parts:

# Header
language: r
sudo: false
dist: trusty
cache: packages
latex: true

#env
env:
global:
- _R_CHECK_FORCE_SUGGESTS_=false
- MAKEFLAGS="-j 2"

jobs:
include:
- addons:
apt:
packages:
- libgoogle-perftools-dev
env:
- KIND=apt
- BUILD_PKGDOWN=true
- addons:
apt:
packages:
- libunwind8-dev
env:
- KIND=github
before_install:
- ( cd /tmp && git clone --depth 1 https://github.com/gperftools/gperftools.git )
- ( cd /tmp/gperftools && ./autogen.sh && ./configure --prefix=${HOME} && make && make install )
- mkdir -p ~/.R
- echo "PKG_CPPFLAGS = -I${HOME}/include" >> ~/.R/Makevars
- echo "LDFLAGS := ${LDFLAGS} -shared -L${HOME}/lib" >> ~/.R/Makevars
- echo "LD_RUN_PATH = ${HOME}/lib" >> ~/.R/Makevars
- echo "export LD_RUN_PATH" >> ~/.R/Makevars

#services
services:
23 changes: 23 additions & 0 deletions gprofiler/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.6)
project(gprofiler)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

set(R_LIBRARY "~/R-library")
set(R_INCLUDE "/usr/share/R/include")

include_directories(${R_LIBRARY}/Rcpp/include)
include_directories(${R_INCLUDE})
include_directories(src)

add_definitions(-D__CLION__)


file(GLOB SOURCE_FILES src/*.cpp src/*.c)
add_library(gprofiler ${SOURCE_FILES})

add_custom_target(
Rinstall
# Using MAKEFLAGS=-j$(nproc) didn't work
COMMAND MAKEFLAGS=-j8 nice R CMD INSTALL --libs-only --no-test-load ${gprofiler_SOURCE_DIR}
)
31 changes: 31 additions & 0 deletions gprofiler/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Package: gprofiler
Title: Joint Profiling of Native and R Code
Version: 0.0-1
Authors@R: c(
person("Kirill", "Müller", role = c("aut", "cre"), email = "[email protected]"),
person("Google, Inc.", role = "cph", comment = "Bundled pprof sources")
)
Description: Simultaneously collects profiling data at both R and native levels
(using 'Rprof' and 'gprofiler') and provides a unified view of the collected data. Work in progress.
License: GPL-3
Encoding: UTF-8
LazyData: true
Date: 2017-01-18
BugReports: https://github.com/r-prof/gprofiler/issues
URL: https://github.com/r-prof/gprofiler, http://r-prof.github.io/gprofiler
LinkingTo: Rcpp
Imports:
dplyr,
profvis,
tidyr,
Rcpp
SystemRequirements: C++11
Suggests:
DBI,
knitr,
rmarkdown,
RSQLite,
testthat
VignetteBuilder: knitr
Roxygen: list(markdown = TRUE)
RoxygenNote: 6.0.1
12 changes: 12 additions & 0 deletions gprofiler/NAMESPACE
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Generated by roxygen2: do not edit by hand

export(callback1_r)
export(callback2_r)
export(callback3_r)
export(get_profiler_traces)
export(show_profiler_pdf)
export(start_profiler)
export(stop_profiler)
importFrom(Rcpp,sourceCpp)
importFrom(tidyr,"%>%")
useDynLib(gprofiler,.registration = TRUE)
11 changes: 11 additions & 0 deletions gprofiler/NEWS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
## gprofiler 0.0-1 (2017-01-18)

Initial release

- Proof of concept.
- Functions `start_profiler()` and `stop_profiler()` to initialize and terminate joint profiling.
- Daisy-chain `SIGPROF` handlers, the `gperftools` profiler offers a filter which can be used to sneak in the call to the original handler installed by `Rprof()`.
- Function `get_profiler_traces()` returns joint profiling data.
- Parsing `gperftools` data with a bleeding-edge version of `pprof` (needs `-traces` switch), Ubuntu binary included.
- Parsing `Rprof` data with a call to an unexported `profvis` function.
- Convenience function `show_profiler_pdf()` shows a PDF of the `gperftools` data.
27 changes: 27 additions & 0 deletions gprofiler/R/RcppExports.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by using Rcpp::compileAttributes() -> do not edit by hand
# Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

callback1_cpp <- function() {
invisible(.Call(`_gprofiler_callback1_cpp`))
}

callback2_cpp <- function() {
invisible(.Call(`_gprofiler_callback2_cpp`))
}

callback3_cpp <- function() {
invisible(.Call(`_gprofiler_callback3_cpp`))
}

init_profiler_impl <- function() {
.Call(`_gprofiler_init_profiler_impl`)
}

start_profiler_impl <- function(ldc, path) {
invisible(.Call(`_gprofiler_start_profiler_impl`, ldc, path))
}

stop_profiler_impl <- function(ldc) {
invisible(.Call(`_gprofiler_stop_profiler_impl`, ldc))
}

23 changes: 23 additions & 0 deletions gprofiler/R/callback.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
sleep <- function() {
stats::runif(2e7)
NULL
}

#' @export
callback1_r <- function() {
sleep()
}

#' @export
callback2_r <- function() {
sleep()
callback1_cpp()
sleep()
}

#' @export
callback3_r <- function() {
sleep()
callback2_cpp()
sleep()
}
3 changes: 3 additions & 0 deletions gprofiler/R/gprofiler-package.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#' @useDynLib gprofiler,.registration = TRUE
#' @importFrom Rcpp sourceCpp
"_PACKAGE"
97 changes: 97 additions & 0 deletions gprofiler/R/profiler.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
.my_env <- new.env(parent = emptyenv())

#' Starts and stops profiling
#'
#' `start_profiler()` initiates profiling.
#'
#' @param path Path to the output file. Other files based on this path
#' may be created.
#'
#' @export
start_profiler <- function(path = "1.prof") {
prof_data <- init_profiler_impl()
utils::Rprof(filename = paste0(path, ".out"), line.profiling = TRUE)
start_profiler_impl(prof_data, path)
.my_env$prof_data <- prof_data
}

#' `stop_profiler()` terminates profiling. The results are available with
#' [get_profiler_traces()].
#'
#' @export
#' @rdname start_profiler
stop_profiler <- function() {
on.exit(.my_env$prof_data <- NULL, add = TRUE)

utils::Rprof(NULL)
stop_profiler_impl(.my_env$prof_data)
}

#' Parse profiler output
#'
#' Combines the profiler output obtained from [start_profiler()] into
#' a nested tibble.
#'
#' @param path The path to the profiler output.
#' @importFrom tidyr %>%
#' @export
get_profiler_traces <- function(path = "1.prof") {
traces <- system2(
get_pprof_path(),
c("-unit", "us", "-lines", "-traces", shQuote(path)),
stdout = TRUE)

pprof_nested <-
strsplit(paste(traces, collapse = "\n"), "\n-+[+]-+\n?")[[1L]][-1L] %>%
tibble::enframe(name = "index", value = "gprofiler") %>%
dplyr::mutate(count = as.numeric(sub("^ +([0-9]+).*$", "\\1", gprofiler)) / 10000) %>%
dplyr::slice(., rep(seq_len(nrow(.)), count)) %>%
dplyr::transmute(time = seq_along(gprofiler), gprofiler = parse_pprof(gprofiler))

rprof_nested <-
profvis:::parse_rprof(paste0(path, ".out"))$prof %>%
tibble::as_tibble() %>%
tidyr::nest(-time, .key = rprof)

stopifnot(pprof_nested$time == rprof_nested$time)
tibble::as_tibble(cbind(pprof_nested, rprof_nested[-1]))
}

parse_pprof <- function(output) {
lapply(output, parse_pprof_one)
}

parse_pprof_one <- function(output_item) {
output_lines <- strsplit(output_item, "\n", fixed = TRUE)[[1]]
rx <- "^(?:| *[0-9][^ ]+) +(?:[?][?]|[<]unknown[>]|(.+) +(?:[?][?]|([^ ]+):([0-9]+)))$"
invalid <- grep(rx, output_lines, invert = TRUE)
if (length(invalid) > 0) {
stop(
"Unexpected profiler output:\n",
paste0(format(invalid), ": ", output_lines[invalid]),
call. = FALSE
)
}

tibble::tibble(
label = gsub(rx, "\\1", output_lines),
filename = gsub(rx, "\\2", output_lines),
line = gsub(rx, "\\3", output_lines)
)
}

#' @export
show_profiler_pdf <- function(path = "1.prof", focus = NULL) {
pprof_exit_code <- system2(
get_pprof_path(),
c("-lines", "-evince", if (!is.null(focus)) paste0("-focus=", focus),
shQuote(path)),
wait = FALSE)
if (pprof_exit_code != 0) {
warning("pprof exited with ", pprof_exit_code)
}
}

get_pprof_path <- function() {
system.file("bin", "pprof", package = utils::packageName())
}
61 changes: 61 additions & 0 deletions gprofiler/README.Rmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
output:
github_document
---

<!-- README.md is generated from README.Rmd. Please edit that file -->

```{r, echo = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>",
fig.path = "README-"
)
```

# gprofiler

The goal of gprofiler is to assist profiling R packages that include native code (C++, C, Fortran, ...).
It collects profiling output simultaneously using [Rprof](https://www.rdocumentation.org/packages/utils/versions/3.3.2/topics/Rprof) and [gperftools](https://github.com/gperftools/gperftools) and provides a unified view of profiling data.
At present this is but a feasibility study: it requires Ubuntu Linux 64-bit and the `libgoogle-perftools-dev` package.


## Example

The following example writes the `iris` data 100 times to an in-memory SQLite database and collects profiling data.
By default, the profiling data are collected in two files, `1.prof` (native) and `1.prof.out` (R).

```{r}
library(DBI)
gprofiler::start_profiler()
con <- dbConnect(RSQLite::SQLite(), ":memory:")
invisible(lapply(1:100, function(x)
dbWriteTable(con, paste0("iris", x), iris)))
dbDisconnect(con)
gprofiler::stop_profiler()
```

A unified view is created with `get_profiler_traces()`. Currently this returns a nested data frame with two list columns, one for the native trace and one for the R trace. Each row represents one sample:

```{r}
gprofiler::get_profiler_traces()
```


Below is another example where an R function calls a C++ function that calls back into R.

```{r}
gprofiler::start_profiler()
gprofiler::callback2_r()
gprofiler::stop_profiler()
gprofiler::get_profiler_traces()
```

Eventually, the result will be an `Rprof`-compatible data format which can be consumed by `profvis` and other existing packages.

### Acknowledgment

This project is being realized with financial support from the

<img src="https://www.r-consortium.org/wp-content/uploads/sites/13/2016/09/RConsortium_Horizontal_Pantone.png" width="400">
Loading

0 comments on commit f42b2c7

Please sign in to comment.