Skip to content

Commit

Permalink
Optimize some memory copies away. (#187)
Browse files Browse the repository at this point in the history
Couple of hotspots found while running malariasimulation with larger
population size. This PR gives a 10% speedup when using population size
of 1M.

The most suprising change comes from a modification to the signature of
Rcpp-exported functions. Some of them used to accept a `const
std::vector` as an argument. However when doing so Rcpp creates an extra
unexpected copy in the wrapping code. We end up with two copies, once
from R memory to an std::vector in the generated code, and a second one
when the method is called. If argument is non-const, Rcpp does not
create the intermediate object and the copy is avoided.

This behaviour comes from the difference between the `InputParameter`
and `ConstInputParameter` classes in Rcpp's [InputParameter.h] file.

Other changes include:
- Return a constant reference from `NumericVariable::get_values`,
  instead of a copy.
- Use `std::move` inside `NumericVariable::queue_update` instead of a
  copy.
- Use `reserve` and `push_back` on a vector instead of filling it with
  zeros to avoid an unnecessary memset.

With this, most operations that work with values require only one copy,
where they used to require two or three.

There are a couple more places that could be optimised further by
working directly with R vectors instead of `std::vector`, but they are
more intrusive changes and don't appear as significantly in
malariasimulation profiles anyway. I've left these out for now.

[InputParameter.h]: https://github.com/RcppCore/Rcpp/blob/c63bae6dea4e3994e6f334612c7837fa18ac1e06/inst/include/Rcpp/InputParameter.h
  • Loading branch information
plietar authored Feb 28, 2024
1 parent ba5052a commit 721837f
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 53 deletions.
19 changes: 9 additions & 10 deletions inst/include/NumericVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ class NumericVariable : public Variable {
NumericVariable(const std::vector<A>& values);
virtual ~NumericVariable() = default;

virtual std::vector<A> get_values() const;
virtual const std::vector<A>& get_values() const;
virtual std::vector<A> get_values(const individual_index_t& index) const;
virtual std::vector<A> get_values(const std::vector<size_t>& index) const;

virtual individual_index_t get_index_of_range(const A a, const A b) const;
virtual size_t get_size_of_range(const A a, const A b) const;

virtual void queue_update(const std::vector<A>& values, const std::vector<size_t>& index);
virtual void queue_update(std::vector<A> values, std::vector<size_t> index);
virtual void queue_extend(const std::vector<A>&);
virtual void queue_shrink(const std::vector<size_t>&);
virtual void queue_shrink(const individual_index_t&);
Expand All @@ -63,7 +63,7 @@ inline NumericVariable<A>::NumericVariable(const std::vector<A>& values)

//' @title get all values
template<class A>
inline std::vector<A> NumericVariable<A>::get_values() const {
inline const std::vector<A>& NumericVariable<A>::get_values() const {
return values;
}

Expand All @@ -73,11 +73,10 @@ inline std::vector<A> NumericVariable<A>::get_values(const individual_index_t& i
if (size() != index.max_size()) {
Rcpp::stop("incompatible size bitset used to get values from NumericVariable");
}
auto result = std::vector<A>(index.size());
auto result_i = 0u;
auto result = std::vector<A>();
result.reserve(index.size());
for (auto i : index) {
result[result_i] = values[i];
++result_i;
result.push_back(values[i]);
}
return result;
}
Expand Down Expand Up @@ -133,8 +132,8 @@ inline size_t NumericVariable<A>::get_size_of_range(
//' @title queue a state update for some subset of individuals
template<class A>
inline void NumericVariable<A>::queue_update(
const std::vector<A>& values,
const std::vector<size_t>& index
std::vector<A> values,
std::vector<size_t> index
) {
if (values.empty()) {
return;
Expand All @@ -148,7 +147,7 @@ inline void NumericVariable<A>::queue_update(
Rcpp::stop("Index out of bounds");
}
}
updates.push({ values, index });
updates.push({ std::move(values), std::move(index) });
}

//' @title apply all queued state updates in FIFO order
Expand Down
48 changes: 24 additions & 24 deletions src/RcppExports.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,12 @@ BEGIN_RCPP
END_RCPP
}
// bitset_sample_vector
void bitset_sample_vector(const Rcpp::XPtr<individual_index_t> b, const std::vector<double> rate);
void bitset_sample_vector(const Rcpp::XPtr<individual_index_t> b, std::vector<double> rate);
RcppExport SEXP _individual_bitset_sample_vector(SEXP bSEXP, SEXP rateSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< const Rcpp::XPtr<individual_index_t> >::type b(bSEXP);
Rcpp::traits::input_parameter< const std::vector<double> >::type rate(rateSEXP);
Rcpp::traits::input_parameter< std::vector<double> >::type rate(rateSEXP);
bitset_sample_vector(b, rate);
return R_NilValue;
END_RCPP
Expand Down Expand Up @@ -383,7 +383,7 @@ BEGIN_RCPP
END_RCPP
}
// double_variable_get_values
std::vector<double> double_variable_get_values(Rcpp::XPtr<DoubleVariable> variable);
const std::vector<double>& double_variable_get_values(Rcpp::XPtr<DoubleVariable> variable);
RcppExport SEXP _individual_double_variable_get_values(SEXP variableSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Expand Down Expand Up @@ -444,58 +444,58 @@ BEGIN_RCPP
END_RCPP
}
// double_variable_queue_fill
void double_variable_queue_fill(Rcpp::XPtr<DoubleVariable> variable, const std::vector<double> value);
void double_variable_queue_fill(Rcpp::XPtr<DoubleVariable> variable, std::vector<double> value);
RcppExport SEXP _individual_double_variable_queue_fill(SEXP variableSEXP, SEXP valueSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<DoubleVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<double> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<double> >::type value(valueSEXP);
double_variable_queue_fill(variable, value);
return R_NilValue;
END_RCPP
}
// double_variable_queue_update
void double_variable_queue_update(Rcpp::XPtr<DoubleVariable> variable, const std::vector<double> value, std::vector<size_t> index);
void double_variable_queue_update(Rcpp::XPtr<DoubleVariable> variable, std::vector<double> value, std::vector<size_t> index);
RcppExport SEXP _individual_double_variable_queue_update(SEXP variableSEXP, SEXP valueSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<DoubleVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<double> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<double> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<size_t> >::type index(indexSEXP);
double_variable_queue_update(variable, value, index);
return R_NilValue;
END_RCPP
}
// double_variable_queue_update_bitset
void double_variable_queue_update_bitset(Rcpp::XPtr<DoubleVariable> variable, const std::vector<double> value, Rcpp::XPtr<individual_index_t> index);
void double_variable_queue_update_bitset(Rcpp::XPtr<DoubleVariable> variable, std::vector<double> value, Rcpp::XPtr<individual_index_t> index);
RcppExport SEXP _individual_double_variable_queue_update_bitset(SEXP variableSEXP, SEXP valueSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<DoubleVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<double> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<double> >::type value(valueSEXP);
Rcpp::traits::input_parameter< Rcpp::XPtr<individual_index_t> >::type index(indexSEXP);
double_variable_queue_update_bitset(variable, value, index);
return R_NilValue;
END_RCPP
}
// double_variable_queue_extend
void double_variable_queue_extend(Rcpp::XPtr<DoubleVariable> variable, std::vector<double>& values);
void double_variable_queue_extend(Rcpp::XPtr<DoubleVariable> variable, std::vector<double> values);
RcppExport SEXP _individual_double_variable_queue_extend(SEXP variableSEXP, SEXP valuesSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<DoubleVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< std::vector<double>& >::type values(valuesSEXP);
Rcpp::traits::input_parameter< std::vector<double> >::type values(valuesSEXP);
double_variable_queue_extend(variable, values);
return R_NilValue;
END_RCPP
}
// double_variable_queue_shrink
void double_variable_queue_shrink(Rcpp::XPtr<DoubleVariable> variable, std::vector<size_t>& index);
void double_variable_queue_shrink(Rcpp::XPtr<DoubleVariable> variable, std::vector<size_t> index);
RcppExport SEXP _individual_double_variable_queue_shrink(SEXP variableSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<DoubleVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< std::vector<size_t>& >::type index(indexSEXP);
Rcpp::traits::input_parameter< std::vector<size_t> >::type index(indexSEXP);
double_variable_queue_shrink(variable, index);
return R_NilValue;
END_RCPP
Expand Down Expand Up @@ -813,7 +813,7 @@ BEGIN_RCPP
END_RCPP
}
// integer_variable_get_values
std::vector<int> integer_variable_get_values(Rcpp::XPtr<IntegerVariable> variable);
const std::vector<int>& integer_variable_get_values(Rcpp::XPtr<IntegerVariable> variable);
RcppExport SEXP _individual_integer_variable_get_values(SEXP variableSEXP) {
BEGIN_RCPP
Rcpp::RObject rcpp_result_gen;
Expand Down Expand Up @@ -922,58 +922,58 @@ BEGIN_RCPP
END_RCPP
}
// integer_variable_queue_fill
void integer_variable_queue_fill(Rcpp::XPtr<IntegerVariable> variable, const std::vector<int> value);
void integer_variable_queue_fill(Rcpp::XPtr<IntegerVariable> variable, std::vector<int> value);
RcppExport SEXP _individual_integer_variable_queue_fill(SEXP variableSEXP, SEXP valueSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<IntegerVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<int> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<int> >::type value(valueSEXP);
integer_variable_queue_fill(variable, value);
return R_NilValue;
END_RCPP
}
// integer_variable_queue_update
void integer_variable_queue_update(Rcpp::XPtr<IntegerVariable> variable, const std::vector<int> value, std::vector<size_t> index);
void integer_variable_queue_update(Rcpp::XPtr<IntegerVariable> variable, std::vector<int> value, std::vector<size_t> index);
RcppExport SEXP _individual_integer_variable_queue_update(SEXP variableSEXP, SEXP valueSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<IntegerVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<int> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<int> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<size_t> >::type index(indexSEXP);
integer_variable_queue_update(variable, value, index);
return R_NilValue;
END_RCPP
}
// integer_variable_queue_update_bitset
void integer_variable_queue_update_bitset(Rcpp::XPtr<IntegerVariable> variable, const std::vector<int> value, Rcpp::XPtr<individual_index_t> index);
void integer_variable_queue_update_bitset(Rcpp::XPtr<IntegerVariable> variable, std::vector<int> value, Rcpp::XPtr<individual_index_t> index);
RcppExport SEXP _individual_integer_variable_queue_update_bitset(SEXP variableSEXP, SEXP valueSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<IntegerVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< const std::vector<int> >::type value(valueSEXP);
Rcpp::traits::input_parameter< std::vector<int> >::type value(valueSEXP);
Rcpp::traits::input_parameter< Rcpp::XPtr<individual_index_t> >::type index(indexSEXP);
integer_variable_queue_update_bitset(variable, value, index);
return R_NilValue;
END_RCPP
}
// integer_variable_queue_extend
void integer_variable_queue_extend(Rcpp::XPtr<IntegerVariable> variable, std::vector<int>& values);
void integer_variable_queue_extend(Rcpp::XPtr<IntegerVariable> variable, std::vector<int> values);
RcppExport SEXP _individual_integer_variable_queue_extend(SEXP variableSEXP, SEXP valuesSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<IntegerVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< std::vector<int>& >::type values(valuesSEXP);
Rcpp::traits::input_parameter< std::vector<int> >::type values(valuesSEXP);
integer_variable_queue_extend(variable, values);
return R_NilValue;
END_RCPP
}
// integer_variable_queue_shrink
void integer_variable_queue_shrink(Rcpp::XPtr<IntegerVariable> variable, std::vector<size_t>& index);
void integer_variable_queue_shrink(Rcpp::XPtr<IntegerVariable> variable, std::vector<size_t> index);
RcppExport SEXP _individual_integer_variable_queue_shrink(SEXP variableSEXP, SEXP indexSEXP) {
BEGIN_RCPP
Rcpp::RNGScope rcpp_rngScope_gen;
Rcpp::traits::input_parameter< Rcpp::XPtr<IntegerVariable> >::type variable(variableSEXP);
Rcpp::traits::input_parameter< std::vector<size_t>& >::type index(indexSEXP);
Rcpp::traits::input_parameter< std::vector<size_t> >::type index(indexSEXP);
integer_variable_queue_shrink(variable, index);
return R_NilValue;
END_RCPP
Expand Down
2 changes: 1 addition & 1 deletion src/bitset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ void bitset_sample(
//[[Rcpp::export]]
void bitset_sample_vector(
const Rcpp::XPtr<individual_index_t> b,
const std::vector<double> rate
std::vector<double> rate
) {
if(b->size() != rate.size()){
Rcpp::stop("vector of probabilties must equal the size of the bitset");
Expand Down
18 changes: 9 additions & 9 deletions src/double_variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Rcpp::XPtr<DoubleVariable> create_double_variable(
}

//[[Rcpp::export]]
std::vector<double> double_variable_get_values(
const std::vector<double>& double_variable_get_values(
Rcpp::XPtr<DoubleVariable> variable
) {
return variable->get_values();
Expand Down Expand Up @@ -67,46 +67,46 @@ size_t double_variable_get_size_of_range(
//[[Rcpp::export]]
void double_variable_queue_fill(
Rcpp::XPtr<DoubleVariable> variable,
const std::vector<double> value
std::vector<double> value
) {
variable->queue_update(value, std::vector<size_t>());
variable->queue_update(std::move(value), std::vector<size_t>());
}

//[[Rcpp::export]]
void double_variable_queue_update(
Rcpp::XPtr<DoubleVariable> variable,
const std::vector<double> value,
std::vector<double> value,
std::vector<size_t> index
) {
decrement(index);
variable->queue_update(value, index);
variable->queue_update(std::move(value), std::move(index));
}

//[[Rcpp::export]]
void double_variable_queue_update_bitset(
Rcpp::XPtr<DoubleVariable> variable,
const std::vector<double> value,
std::vector<double> value,
Rcpp::XPtr<individual_index_t> index
) {
if (index->max_size() != variable->size()) {
Rcpp::stop("incompatible size bitset used to queue update for DoubleVariable");
}
auto index_vec = bitset_to_vector_internal(*index, false);
variable->queue_update(value, index_vec);
variable->queue_update(std::move(value), std::move(index_vec));
}

//[[Rcpp::export]]
void double_variable_queue_extend(
Rcpp::XPtr<DoubleVariable> variable,
std::vector<double>& values
std::vector<double> values
) {
variable->queue_extend(values);
}

//[[Rcpp::export]]
void double_variable_queue_shrink(
Rcpp::XPtr<DoubleVariable> variable,
std::vector<size_t>& index
std::vector<size_t> index
) {
decrement(index);
variable->queue_shrink(index);
Expand Down
18 changes: 9 additions & 9 deletions src/integer_variable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Rcpp::XPtr<IntegerVariable> create_integer_variable(
}

//[[Rcpp::export]]
std::vector<int> integer_variable_get_values(
const std::vector<int>& integer_variable_get_values(
Rcpp::XPtr<IntegerVariable> variable
) {
return variable->get_values();
Expand Down Expand Up @@ -106,43 +106,43 @@ size_t integer_variable_get_size_of_range(
//[[Rcpp::export]]
void integer_variable_queue_fill(
Rcpp::XPtr<IntegerVariable> variable,
const std::vector<int> value
std::vector<int> value
) {
variable->queue_update(value, std::vector<size_t>());
variable->queue_update(std::move(value), std::vector<size_t>());
}

//[[Rcpp::export]]
void integer_variable_queue_update(
Rcpp::XPtr<IntegerVariable> variable,
const std::vector<int> value,
std::vector<int> value,
std::vector<size_t> index
) {
decrement(index);
variable->queue_update(value, index);
variable->queue_update(std::move(value), std::move(index));
}

//[[Rcpp::export]]
void integer_variable_queue_update_bitset(
Rcpp::XPtr<IntegerVariable> variable,
const std::vector<int> value,
std::vector<int> value,
Rcpp::XPtr<individual_index_t> index
) {
auto index_vec = bitset_to_vector_internal(*index, false);
variable->queue_update(value, index_vec);
variable->queue_update(std::move(value), std::move(index_vec));
}

//[[Rcpp::export]]
void integer_variable_queue_extend(
Rcpp::XPtr<IntegerVariable> variable,
std::vector<int>& values
std::vector<int> values
) {
variable->queue_extend(values);
}

//[[Rcpp::export]]
void integer_variable_queue_shrink(
Rcpp::XPtr<IntegerVariable> variable,
std::vector<size_t>& index
std::vector<size_t> index
) {
decrement(index);
variable->queue_shrink(index);
Expand Down

0 comments on commit 721837f

Please sign in to comment.