复制一个 std::function 有多贵?
How expensive is to copy an std::function?
虽然std::function
是可移动的,但在某些情况下是不可能或不方便的。复制它会受到严重处罚吗?
它是否可能取决于捕获变量的大小(如果它是使用 lambda 表达式创建的)?它取决于实现吗?
它确实依赖于实现。它还取决于您在 std::function
中存储的内容。它肯定不像简单地复制 C 风格的函数指针那么便宜。
最好的办法是尝试按照您认为清晰方便的方式编写代码,然后,如果它 运行 不够快,请对其进行概要分析。如果你需要极致的性能,你可能会发现 std::function
根本不适合。
std::function
通常实现为值语义、小缓冲区优化、虚拟调度、类型擦除 class.
这意味着如果你的状态很小,复制将不涉及堆分配(除了在状态的复制构造函数内)和一些间接的(找到如何复制这个特定状态)。
如果您的状态很大(例如,在当前 MSVC 上大于两个 std::string
),则需要额外的堆分配来存储状态。
这不是您想在每帧每个像素的基础上做的事情,但它并不是非常昂贵。
您的特定编译器和库版本如何实现 std::function
可能会有所不同,但我不知道与上述版本有任何显着差异。
所以,在下面的代码中:
std::function<std::string()> f = [s = "hello"s]{ return s; };
复制 f
将涉及 MSVC 上的 0 堆分配。
鉴于此代码:
std::function<std::string()> g = [a = "a"s, b = "b"s, c = "c"s]{ return a+b+c; };
是否需要堆分配才能复制 g
。
(是的,这两个都是非常愚蠢的函数对象。)
std::function
中的某些情况(函数指针、成员函数指针)应用小型缓冲区优化 (SBO) 是有效的要求,因为它们预计不会在分配内存时失败.一旦编写了一个 SBO 案例,使其更通用并不难,因此大多数 std::function
实现将小对象 "inside themselves" 和较大的对象存储在堆上。
然而,这个阈值没有被标准指定,它对性能成本很重要,所以如果性能真的很重要,你就必须分析以确保你的实现做到了。
虽然std::function
是可移动的,但在某些情况下是不可能或不方便的。复制它会受到严重处罚吗?
它是否可能取决于捕获变量的大小(如果它是使用 lambda 表达式创建的)?它取决于实现吗?
它确实依赖于实现。它还取决于您在 std::function
中存储的内容。它肯定不像简单地复制 C 风格的函数指针那么便宜。
最好的办法是尝试按照您认为清晰方便的方式编写代码,然后,如果它 运行 不够快,请对其进行概要分析。如果你需要极致的性能,你可能会发现 std::function
根本不适合。
std::function
通常实现为值语义、小缓冲区优化、虚拟调度、类型擦除 class.
这意味着如果你的状态很小,复制将不涉及堆分配(除了在状态的复制构造函数内)和一些间接的(找到如何复制这个特定状态)。
如果您的状态很大(例如,在当前 MSVC 上大于两个 std::string
),则需要额外的堆分配来存储状态。
这不是您想在每帧每个像素的基础上做的事情,但它并不是非常昂贵。
您的特定编译器和库版本如何实现 std::function
可能会有所不同,但我不知道与上述版本有任何显着差异。
所以,在下面的代码中:
std::function<std::string()> f = [s = "hello"s]{ return s; };
复制 f
将涉及 MSVC 上的 0 堆分配。
鉴于此代码:
std::function<std::string()> g = [a = "a"s, b = "b"s, c = "c"s]{ return a+b+c; };
是否需要堆分配才能复制 g
。
(是的,这两个都是非常愚蠢的函数对象。)
std::function
中的某些情况(函数指针、成员函数指针)应用小型缓冲区优化 (SBO) 是有效的要求,因为它们预计不会在分配内存时失败.一旦编写了一个 SBO 案例,使其更通用并不难,因此大多数 std::function
实现将小对象 "inside themselves" 和较大的对象存储在堆上。
然而,这个阈值没有被标准指定,它对性能成本很重要,所以如果性能真的很重要,你就必须分析以确保你的实现做到了。