Skip to content

Commit

Permalink
✨ gains sum_with_threads (#19)
Browse files Browse the repository at this point in the history
* remove boilerplate

* add failing test

* gains `sum_with_threads_impl`

* add more tests

* handle empty vector case

* remove superfluous comments

* add R wrapper around implementation

* add savvy-test feature

* update function name

* savvy::savvy_update()

* devtools::document()

* add benchmarking to README

* add an R test
  • Loading branch information
jdhoffa authored Dec 13, 2024
1 parent 2ec66a2 commit aeb75a5
Show file tree
Hide file tree
Showing 16 changed files with 227 additions and 241 deletions.
4 changes: 1 addition & 3 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,5 @@

S3method("$<-",savvy_blazr__sealed)
S3method("[[<-",savvy_blazr__sealed)
S3method(print,Person__bundle)
export(int_times_int)
export(to_upper)
export(sum_with_threads)
useDynLib(blazr, .registration = TRUE)
65 changes: 7 additions & 58 deletions R/000-wrappers.R
Original file line number Diff line number Diff line change
Expand Up @@ -37,67 +37,16 @@ NULL
stop(class, " cannot be modified", call. = FALSE)
}

#' Convert Input To Upper-Case
#' Calculate the sum of a vector of integers using multiple threads.
#'
#' @param x A character vector.
#' @returns A character vector with upper case version of the input.
#' @export
`to_upper` <- function(`x`) {
.Call(savvy_to_upper__impl, `x`)
}

#' Multiply Input By Another Input
#' @param x A vector of integers to sum over.
#' @param n The number of threads used to compute this calculation (int).
#'
#' @return The sum of all elements of the input vector.
#'
#' @param x An integer vector.
#' @param y An integer to multiply.
#' @returns An integer vector with values multiplied by `y`.
#' @export
`int_times_int` <- function(`x`, `y`) {
.Call(savvy_int_times_int__impl, `x`, `y`)
`sum_with_threads` <- function(`x`, `n`) {
.Call(savvy_sum_with_threads__impl, `x`, `n`)
}

### wrapper functions for Person

`Person_set_name` <- function(self) {
function(`name`) {
invisible(.Call(savvy_Person_set_name__impl, `self`, `name`))
}
}

`Person_name` <- function(self) {
function() {
.Call(savvy_Person_name__impl, `self`)
}
}

`.savvy_wrap_Person` <- function(ptr) {
e <- new.env(parent = emptyenv())
e$.ptr <- ptr
e$`set_name` <- `Person_set_name`(ptr)
e$`name` <- `Person_name`(ptr)

class(e) <- c("Person", "savvy_blazr__sealed")
e
}



`Person` <- new.env(parent = emptyenv())

### associated functions for Person

`Person`$`new` <- function() {
.savvy_wrap_Person(.Call(savvy_Person_new__impl))
}

`Person`$`associated_function` <- function() {
.Call(savvy_Person_associated_function__impl)
}


class(`Person`) <- c("Person__bundle", "savvy_blazr__sealed")

#' @export
`print.Person__bundle` <- function(x, ...) {
cat('Person')
}
47 changes: 45 additions & 2 deletions README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,53 @@ pak::pak("r-staceans/blazr")

## Example

This is just a dummy example:
Here's a simple example, computing the sum of a vector of integers, using multiple threads:

```{r example}
library(blazr)
blazr::to_upper("hello, world")
create_int_vector <- function(n) {
set.seed(42)
sample.int(100, n, replace = TRUE)
}
n <- 1e8
x <- create_int_vector(n)
blazr::sum_with_threads(
x,
n = 2L
)
```

## Benchmarking

When running this sum against very large numbers, we can see the performance benefits of using multiple threads:

```{r benchmark}
library(bench)
library(ggplot2)
results <- bench::press(
size = c(1e7, 1e8, 1e9),
{
x = create_int_vector(n)
bench::mark(
single_thread = {
blazr::sum_with_threads(
x,
n = 1L
)
},
multi_thread = {
blazr::sum_with_threads(
x,
n = 4L
)
}
)
})
ggplot2::autoplot(results)
```
59 changes: 56 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,64 @@ pak::pak("r-staceans/blazr")

## Example

This is just a dummy example:
Here’s a simple example, computing the sum of a vector of integers,
using multiple threads:

``` r
library(blazr)

blazr::to_upper("hello, world")
#> [1] "HELLO, WORLD"
create_int_vector <- function(n) {
set.seed(42)
sample.int(100, n, replace = TRUE)
}

n <- 1e8

x <- create_int_vector(n)

blazr::sum_with_threads(
x,
n = 2L
)
#> [1] 754832969
```

## Benchmarking

When running this sum against very large numbers, we can see the
performance benefits of using multiple threads:

``` r
library(bench)
library(ggplot2)

results <- bench::press(
size = c(1e7, 1e8, 1e9),
{
x = create_int_vector(n)
bench::mark(
single_thread = {
blazr::sum_with_threads(
x,
n = 1L
)
},
multi_thread = {
blazr::sum_with_threads(
x,
n = 4L
)
}
)
})
#> Running with:
#> size
#> 1 10000000
#> 2 100000000
#> 3 1000000000

ggplot2::autoplot(results)
#> Loading required namespace: tidyr
```

<img src="man/figures/README-benchmark-1.png" width="100%" />
2 changes: 2 additions & 0 deletions man/blazr-package.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added man/figures/README-benchmark-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 0 additions & 19 deletions man/int_times_int.Rd

This file was deleted.

19 changes: 19 additions & 0 deletions man/sum_with_threads.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 0 additions & 17 deletions man/to_upper.Rd

This file was deleted.

36 changes: 3 additions & 33 deletions src/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,14 @@ SEXP handle_result(SEXP res_) {
return (SEXP)res;
}

SEXP savvy_to_upper__impl(SEXP c_arg__x) {
SEXP res = savvy_to_upper__ffi(c_arg__x);
return handle_result(res);
}

SEXP savvy_int_times_int__impl(SEXP c_arg__x, SEXP c_arg__y) {
SEXP res = savvy_int_times_int__ffi(c_arg__x, c_arg__y);
return handle_result(res);
}

SEXP savvy_Person_new__impl(void) {
SEXP res = savvy_Person_new__ffi();
return handle_result(res);
}

SEXP savvy_Person_set_name__impl(SEXP self__, SEXP c_arg__name) {
SEXP res = savvy_Person_set_name__ffi(self__, c_arg__name);
return handle_result(res);
}

SEXP savvy_Person_name__impl(SEXP self__) {
SEXP res = savvy_Person_name__ffi(self__);
return handle_result(res);
}

SEXP savvy_Person_associated_function__impl(void) {
SEXP res = savvy_Person_associated_function__ffi();
SEXP savvy_sum_with_threads__impl(SEXP c_arg__x, SEXP c_arg__n) {
SEXP res = savvy_sum_with_threads__ffi(c_arg__x, c_arg__n);
return handle_result(res);
}


static const R_CallMethodDef CallEntries[] = {
{"savvy_to_upper__impl", (DL_FUNC) &savvy_to_upper__impl, 1},
{"savvy_int_times_int__impl", (DL_FUNC) &savvy_int_times_int__impl, 2},
{"savvy_Person_new__impl", (DL_FUNC) &savvy_Person_new__impl, 0},
{"savvy_Person_set_name__impl", (DL_FUNC) &savvy_Person_set_name__impl, 2},
{"savvy_Person_name__impl", (DL_FUNC) &savvy_Person_name__impl, 1},
{"savvy_Person_associated_function__impl", (DL_FUNC) &savvy_Person_associated_function__impl, 0},
{"savvy_sum_with_threads__impl", (DL_FUNC) &savvy_sum_with_threads__impl, 2},
{NULL, NULL, 0}
};

Expand Down
16 changes: 8 additions & 8 deletions src/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ crate-type = ["staticlib", "lib"]
[dependencies]
savvy = "*"

[features]
savvy-test = []

[profile.release]
# By default, on release build, savvy terminates the R session when a panic
# occurs. This is the right behavior in that a panic means such a fatal event
Expand Down
9 changes: 1 addition & 8 deletions src/rust/api.h
Original file line number Diff line number Diff line change
@@ -1,8 +1 @@
SEXP savvy_to_upper__ffi(SEXP c_arg__x);
SEXP savvy_int_times_int__ffi(SEXP c_arg__x, SEXP c_arg__y);

// methods and associated functions for Person
SEXP savvy_Person_new__ffi(void);
SEXP savvy_Person_set_name__ffi(SEXP self__, SEXP c_arg__name);
SEXP savvy_Person_name__ffi(SEXP self__);
SEXP savvy_Person_associated_function__ffi(void);
SEXP savvy_sum_with_threads__ffi(SEXP c_arg__x, SEXP c_arg__n);
Loading

0 comments on commit aeb75a5

Please sign in to comment.