C ++ 11 lambda实现和内存模型 [英] C++11 lambda implementation and memory model

查看:153
本文介绍了C ++ 11 lambda实现和内存模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要一些关于如何正确思考C ++ 11闭包和 std :: function 的信息,如何实现它们以及如何处理内存。



虽然我不相信过早的优化,我有一个习惯,在编写新代码时仔细考虑我的选择的性能影响。我还做了大量的实时编程,例如。在微控制器和音频系统,其中要避免非确定性内存分配/释放暂停。



因此,我想开发一个更好地了解什么时候使用或者不使用C ++ lambdas。



我目前的理解是没有捕获闭包的lambda就像一个C回调。但是,当通过值或引用捕获环境时,会在堆栈上创建一个匿名对象。当一个值闭包必须从一个函数返回时,它将它封装在 std :: function 中。在这种情况下,闭包内存会发生什么?它是从堆栈复制到堆?每当 std :: function 被释放时,它被释放,即它是一个 std :: shared_ptr



我想象在实时系统中,我可以建立一个lambda函数链,将B作为一个连续参数传递给A,以便处理管道 A-> B 。在这种情况下,A和B闭包将被分配一次。虽然我不知道这些是否将分配在堆栈或堆。然而,一般来说,这似乎在实时系统中使用是安全的。另一方面,如果B构造一些lambda函数C,它返回,那么C的内存将被重复分配和释放,这对于实时使用是不可接受的。



在伪代码中,一个DSP循环,我认为是实时安全的。我想要执行处理块A和然后B,其中A调用其参数。这两个函数都返回 std :: function 对象,所以 f 将是 std ::函数对象,其环境存储在堆上:

  auto f = A ; // A返回一个调用B的函数
// A返回的函数的内存是在堆上吗?
//注意A和B可以通过可变的值闭包保持状态
//!
for(t = 0; t <1000; t ++){
y = f(t)
}

我认为在实时代码中可能不好用:

  for(t = 0; t <1000; t ++){
y = A(B)(t);
}

还有一个我认为堆栈内存可能用于闭包: p>

  freq = 220; 
A = 2;
for(t = 0; t <1000; t ++){
y = [=](int t){return sin(t * freq)* A; }
}

在后一种情况下,闭包是在循环的每次迭代时构建的,但不像前面的例子,它是便宜的,因为它就像一个函数调用,没有堆分配。此外,我不知道编译器是否可以解除闭包并进行内联优化。



这是正确的吗?谢谢。

解决方案


我目前的理解是,没有捕获闭包的lambda完全像一个C回调。但是,当通过值或引用捕获环境时,会在堆栈上创建一个匿名对象。


它始终是在堆栈上创建的具有未知类型的C ++对象。无捕获lambda可以被转换为函数指针(虽然它是否适合C调用约定是实现相关的),但这并不意味着它是 函数指针。


当一个值闭包必须从一个函数返回时,将它包装在std :: function中。在这种情况下,闭包内存会发生什么?


在C ++ 11中,lambda并不是什么特别的。它是一个对象像任何其他对象。 lambda表达式产生一个临时变量,可用于初始化堆栈上的变量:

  auto lamb = [] ){return 5;}; 

lamb 是一个堆栈对象。它有一个构造函数和析构函数。它将遵循所有的C ++规则。 lamb 的类型将包含捕获的值/引用;它们将是该对象的成员,就像任何其他类型的任何其他对象成员一样。



您可以将其提供给 std :: function

  auto func_lamb = std :: function< int()>(lamb) 

在这种情况下,它将获得 code> lamb 。如果 lamb 通过值捕获了任何东西,那么这些值将有两个副本;一个在 lamb 和一个在 func_lamb



当当前作用域结束时,根据清除堆栈变量的规则,将会销毁 func_lamb ,其后是 lamb



您可以轻松地在堆上分配一个:

  auto func_lamb_ptr = new std :: function< int()>(lamb); 

完全在 std :: function go是与实现相关的,但 std :: function 使用的类型擦除通常需要至少一个内存分配。这是为什么 std :: function 的构造函数可以接受分配器。


每当std :: function被释放时,它被释放,即它是否像std :: shared_ptr一样被引用计数?


code> std :: function 存储其内容的副本。像几乎每个标准库C ++类型一样,函数使用值语义。因此,它是可复制的;当它被复制时,新的函数对象是完全独立的。它也是可移动的,所以任何内部分配都可以被适当地传输,而不需要更多的分配和复制。



因此不需要引用计数。



假设内存分配等同于不能在实时代码中使用,所有你说的是正确的。


I would like some information on how to correctly think about C++11 closures and std::function in terms of how they are implemented and how memory is handled.

Although I don't believe in premature optimisation, I do have a habit of carefully considering the performance impact of my choices while writing new code. I also do a fair amount of real-time programming, e.g. on microcontrollers and for audio systems, where non-deterministic memory allocation/deallocation pauses are to be avoided.

Therefore I'd like to develop a better understanding of when to use or not use C++ lambdas.

My current understanding is that a lambda with no captured closure is exactly like a C callback. However, when the environment is captured either by value or by reference, an anonymous object is created on the stack. When a value-closure must be returned from a function, one wraps it in std::function. What happens to the closure memory in this case? Is it copied from the stack to the heap? Is it freed whenever the std::function is freed, i.e., is it reference-counted like a std::shared_ptr?

I imagine that in a real-time system I could set up a chain of lambda functions, passing B as a continuation argument to A, so that a processing pipeline A->B is created. In this case, the A and B closures would be allocated once. Although I'm not sure whether these would be allocated on the stack or the heap. However in general this seems safe to use in a real-time system. On the other hand if B constructs some lambda function C, which it returns, then the memory for C would be allocated and deallocated repeatedly, which would not be acceptable for real-time usage.

In pseudo-code, a DSP loop, which I think is going to be real-time safe. I want to perform processing block A and then B, where A calls its argument. Both these functions return std::function objects, so f will be a std::function object, where its environment is stored on the heap:

auto f = A(B);  // A returns a function which calls B
                // Memory for the function returned by A is on the heap?
                // Note that A and B may maintain a state
                // via mutable value-closure!
for (t=0; t<1000; t++) {
    y = f(t)
}

And one which I think might be bad to use in real-time code:

for (t=0; t<1000; t++) {
    y = A(B)(t);
}

And one where I think stack memory is likely used for the closure:

freq = 220;
A = 2;
for (t=0; t<1000; t++) {
    y = [=](int t){ return sin(t*freq)*A; }
}

In the latter case the closure is constructed at each iteration of the loop, but unlike the previous example it is cheap because it is just like a function call, no heap allocations are made. Moreover, I wonder if a compiler could "lift" the closure and make inlining optimisations.

Is this correct? Thank you.

解决方案

My current understanding is that a lambda with no captured closure is exactly like a C callback. However, when the environment is captured either by value or by reference, an anonymous object is created on the stack.

No; it is always a C++ object with an unknown type, created on the stack. A capture-less lambda can be converted into a function pointer (though whether it is suitable for C calling conventions is implementation dependent), but that doesn't mean it is a function pointer.

When a value-closure must be returned from a function, one wraps it in std::function. What happens to the closure memory in this case?

A lambda isn't anything special in C++11. It's an object like any other object. A lambda expression results in a temporary, which can be used to initialize a variable on the stack:

auto lamb = []() {return 5;};

lamb is a stack object. It has a constructor and destructor. And it will follow all of the C++ rules for that. The type of lamb will contain the values/references that are captured; they will be members of that object, just like any other object members of any other type.

You can give it to a std::function:

auto func_lamb = std::function<int()>(lamb);

In this case, it will get a copy of the value of lamb. If lamb had captured anything by value, there would be two copies of those values; one in lamb, and one in func_lamb.

When the current scope ends, func_lamb will be destroyed, followed by lamb, as per the rules of cleaning up stack variables.

You could just as easily allocate one on the heap:

auto func_lamb_ptr = new std::function<int()>(lamb);

Exactly where the memory for the contents of a std::function goes is implementation-dependent, but the type-erasure employed by std::function generally requires at least one memory allocation. This is why std::function's constructor can take an allocator.

Is it freed whenever the std::function is freed, i.e., is it reference-counted like a std::shared_ptr?

std::function stores a copy of its contents. Like virtually every standard library C++ type, function uses value semantics. Thus, it is copyable; when it is copied, the new function object is completely separate. It is also moveable, so any internal allocations can be transferred appropriately without needing more allocating and copying.

Thus there is no need for reference counting.

Everything else you state is correct, assuming that "memory allocation" equates to "bad to use in real-time code".

这篇关于C ++ 11 lambda实现和内存模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆