Skip to content

Efficiency issue in boxing std::function objects #29

Description

@camio

The code for boxing callables looks something like this:

rust::Box<::rust::Dyn<::rust::Fn<::int32_t, ::int32_t>>>
rust::Box<::rust::Dyn<::rust::Fn<::int32_t, ::int32_t>>>::make_box(
    ::std::function<::int32_t(::int32_t)> f) {
  auto data = new ::std::function<::int32_t(::int32_t)>(f);
  rust::Box<::rust::Dyn<::rust::Fn<::int32_t, ::int32_t>>> o;
  ::rust::__zngur_internal_assume_init(o);
  __zngur_i32_i32_c11(
      reinterpret_cast<uint8_t *>(data),
      [](uint8_t *d) {
        delete reinterpret_cast<::std::function<::int32_t(::int32_t)> *>(d);
      },
      [](uint8_t *d, uint8_t *i0, uint8_t *o) {
        auto dd = reinterpret_cast<::std::function<::int32_t(::int32_t)> *>(d);
        ::int32_t oo =
            (*dd)(::rust::__zngur_internal_move_from_rust<::int32_t>(i0));
        ::rust::__zngur_internal_move_to_rust<::int32_t>(o, oo);
      },
      ::rust::__zngur_internal_data_ptr(o));
  return o;
}

There are potentially three allocations here. The first is when the std::function argument is initialized, the second happens when data is allocated, and the third is when *data is initialized using its copy constructor.

An easy way to avoid one of these allocations is to use std::move when using new like this:

  auto data = new ::std::function<::int32_t(::int32_t)>( std::move(f) );

However, I think it may be better to use a template interface:

template<typename... Args>
rust::Box<::rust::Dyn<::rust::Fn<::int32_t, ::int32_t>>>
rust::Box<::rust::Dyn<::rust::Fn<::int32_t, ::int32_t>>>::make_box( Args&&... args) {
  auto data = new ::std::function<::int32_t(::int32_t)>(::std::forward<Args>(args)...);
  // ...
  return o;
}

This way you avoid the move and, once C++26 drops, you can switch to std::copyable_function which will allow your callers to use std::in_place_type_t as an argument and potentially gain further efficiency gains.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions