sTD ::功能的替代方案,用于收集可可的功能



除了求助于 std::function外,还有其他方法可以存储均匀的可呼叫集吗?即,在以下代码中替换类型T

using T = std::function<void(int)>;
std::vector<T> v{some_lambda, some_fn_ptr, some_pmf, some_functor};

还有其他东西?

将单个可召唤作为参数传递给高阶函数时,我会尽可能使用模板来避免std::function的开销。但是对于收藏,我不知道我能做什么。

直接类型的最大架空降低源是内联函数的能力。在具有重复应用的紧密循环中,有时可以对嵌入的函数进行矢量化或以其他方式进行优化。

std::function的第二个开销源是它使用虚拟功能表的事实。这会导致两个"随机访问"内存查找 - 一个用于VTable,另一个遵循VTable上的指针。如果这些不在先前使用中的缓存中,则相当昂贵。但是,如果它们处于缓存中,最终根本就不是昂贵的(一些说明)。

std::function的最后一个开销来源是内存分配。如果存储在std::function中的对象很大(以MSVC为例,比sizeof(std::string)*2大,其中std::string本身使用SBO,因此大小适中),则会发生堆分配。因此,每当复制或创建std::function时,成本都相当高。

可以缓解这些中的每一个。

自定义std::function克隆可以使用无Vtable Invoke型擦除来降低#2的成本。这有尺寸成本。

可以编写不存储可可的function_ref类型。这些存储一个void*和相当于vtable(或直接指向方法的指针)。另外,可以编写具有自定义存储大小的std::function克隆,并且可以编写拒绝堆分配。要么以灵活性和/或缺乏价值语义为代价。

可以很好地缓解#3。

第一个是最难缓解的。

如果您知道将使用您的可调用执行哪些操作,而不是删除通用的援引,则可以在上下文中删除以调用。

为例,假设您有一个像素操作。在图像的每个像素上调用std::function都会有很多开销。

但是,如果我们不再删除每个像素呼叫(或也是),我们可以删除每个像素运行的呼叫,我们现在可以一般地存储我们的可呼叫,并且开销从每个像素到人均canline或每个图像!

现在可以在紧密的循环中看到可呼叫,因此可以通过编译器将其嵌入和矢量化,并且每个扫描线仅完成一次。

您可能会变得更加豪华,甚至可以用linestride擦除扫描线。或有几个擦除,一种用于零额外的列板的扫描线,一种用于倒置扫描线,另一个用于非零列板等。

这些都有成本,而在开发时间至少。仅当您进行了测试并确认std::function实际上引起问题时才走下去。

存在std::function的开销,因为它是一个值类型。它在内部提供的函数(当然可以从中移动)存储副本。它确实键入擦除,但是由于它可以存储一个任意侧的对象,因此必须能够分配内存。

因此,如果您的需求不需要该函数实际存储一个对象,则只能引用一个继续存在的对象,那么您会没事的。您可以找到几种类型的实现,通常称为function_ref。这样的类型永远不会分配内存;虽然他们的开销不是零,但不如function

但是,这些是对现有函数的参考。因此,对于函子,它们引用了真实的C 对象(而不是函数/成员指针)。这会引起潜在的终身问题。要立即进行回调(您将它们传递到只能在其持续时间内调用该回调的函数),这并不是什么问题。但是就您而言,这可能不会有用。

最终,如果您无法解决终身问题,那么使用std::function的开销实际上对您很有用。最好接受。

仍然允许您在此处指定的所有替代方案

 std::vector<T> v{some_lambda, some_fn_ptr, some_pmf, some_functor};

您肯定可以创建自己的模板声明,以赶上所有这些模板。尽管我看不出它可能比std::function更好或更好

最新更新