C++11 лямбда-реализация и модель памяти
Я хотел бы получить некоторую информацию о том, как правильно думать о закрытиях C++11 и std::function
С точки зрения того, как они реализованы и как обрабатывается память.
хотя я не верю в преждевременную оптимизацию, у меня есть привычка тщательно рассматривать влияние производительности моего выбора при написании нового кода. Я также делаю изрядное количество программирования в реальном времени, например, на микроконтроллерах и для аудиосистем, где недетерминированное выделение/освобождение памяти пауз следует избегать.
поэтому я хотел бы лучше понять, когда использовать или не использовать лямбды C++.
мое текущее понимание заключается в том, что лямбда без захваченного закрытия точно так же, как обратный вызов C. Однако, когда среда захватывается либо по значению, либо по ссылке, в стеке создается анонимный объект. Когда значение-закрытие должно быть возвращено из функции, один обертывает его в std::function
. Что происходит с памятью закрытия в это дело? Он копируется из стека в кучу? Это освобождается всякий раз, когда std::function
освобождается, т. е. это ссылка расценено как std::shared_ptr
?
Я полагаю, что в системе реального времени я мог бы настроить цепочку лямбда-функций, передав B в качестве аргумента продолжения A, так что конвейер обработки это. В этом случае закрытие A и B будет выделено один раз. Хотя я не уверен, будут ли они выделены в стеке или куче. Однако в вообще это кажется безопасным для использования в системах реального времени. С другой стороны, если B создает некоторую лямбда-функцию C, которую он возвращает, то память для C будет выделяться и освобождаться повторно, что было бы неприемлемо для использования в реальном времени.
в псевдокоде, цикл DSP, который, я думаю, будет безопасным в режиме реального времени. Я хочу выполнить обработку блока A, а затем B, где A вызывает его аргумент. Обе эти функции возвращают std::function
объекты, так f
будет std::function
объект, где его окружение хранится в куче:
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)
}
и тот, который я думаю, может быть плохо использовать в коде реального времени:
for (t=0; t<1000; t++) {
y = A(B)(t);
}
и тот, где я думаю, что память стека, вероятно, используется для закрытия:
freq = 220;
A = 2;
for (t=0; t<1000; t++) {
y = [=](int t){ return sin(t*freq)*A; }
}
в последнем случае закрытия создается на каждой итерации цикла, но в отличие от предыдущего примера это дешево, потому что это просто как вызов функции, без выделений сделаны. Более того, мне интересно, если компилятор может "поднять" закрытие и сделать встроенные оптимизации.
это правильно? Спасибо.
1 ответ:
мое текущее понимание заключается в том, что лямбда без захваченного закрытия точно так же, как обратный вызов C. Однако, когда среда захватывается либо по значению, либо по ссылке, в стеке создается анонимный объект.
нет, это всегда объект C++ с неизвестным типом, созданный в стеке. Лямбда без захвата может быть преобразовать в указатель функции (хотя подходит ли он для соглашений о вызовах C зависит от реализации), но это не значит, что и указатель на функцию.
когда значение-закрытие должно быть возвращено из функции, один обертывает его в std:: function. Что происходит с памятью закрытия в этом случае?
лямбда-это не что-то особенное в C++11. Это такой же объект, как и любой другой. Лямбда-выражение приводит к временному, который может быть использован для инициализации переменной на стек:
auto lamb = []() {return 5;};
lamb
- это объект стека. Он имеет конструктор и деструктор. И он будет следовать всем правилам C++ для этого. Типlamb
будет содержать захваченные значения / ссылки; они будут членами этого объекта, как и любые другие члены объекта любого другого типа.вы можете дать его
std::function
:auto func_lamb = std::function<int()>(lamb);
в этом случае он получит скопировать стоимостью
lamb
. Еслиlamb
захватили что-нибудь по значению, было бы две копии этих значений; вlamb
и один вfunc_lamb
.когда текущая область заканчивается,
func_lamb
будет уничтожен, а затемlamb
, согласно правилам очистки переменных стека.вы могли бы так же легко выделить один в куче:
auto func_lamb_ptr = new std::function<int()>(lamb);
именно там, где память на содержание
std::function
goes зависит от реализации, но тип-стирание, используемоеstd::function
как правило, требует по крайней мере одно выделение памяти. Вот почемуstd::function
конструктор может принимать распределитель.освобождается ли он всякий раз, когда функция std::освобождается, т. е. считается ли она ссылочной, как std::shared_ptr?
std::function
хранит a скопировать его содержание. Как и практически все стандартные библиотеки типа C++,function
использует значение семантики. Таким образом, он копируется; когда он копируется, новыйfunction
объект полностью отделен. Он также подвижен, поэтому любые внутренние распределения могут быть переданы соответствующим образом без необходимости дополнительного выделения и копирования.таким образом, нет необходимости в подсчете ссылок.
все остальное вы заявляете правильно, предполагая, что" выделение памяти "приравнивается к"плохо использовать в коде реального времени".