Я хотів би отримати деяку інформацію про те, як правильно думати про закриття C ++ 11, а також std::function
про те, як вони реалізовані та як обробляється пам’ять.
Незважаючи на те, що я не вірю в передчасну оптимізацію, я маю звичку ретельно розглядати вплив мого вибору на продуктивність під час написання нового коду. Я також роблю велику кількість програм у реальному часі, наприклад, на мікроконтролерах та аудіосистемах, де слід уникати недетермінованих пауз розподілу / вивільнення пам'яті.
Тому я хотів би краще зрозуміти, коли використовувати чи не використовувати лямбди С ++.
На моєму теперішньому розумінні, лямбда без захопленого замикання точно відповідає зворотному виклику C. Однак, коли середовище фіксується або за значенням, або за посиланням, у стеку створюється анонімний об'єкт. Коли закриття значення повинно бути повернуто з функції, воно обертає його std::function
. Що в цьому випадку відбувається із закритою пам’яттю? Це скопійовано зі стеку в купу? Чи звільняється воно щоразу, коли std::function
звільняється, тобто чи вважається воно посиланням як a std::shared_ptr
?
Я уявляю, що в системі реального часу я міг би створити ланцюжок лямбда-функцій, передаючи B як аргумент продовження A, так що A->B
буде створений конвеєр обробки . У цьому випадку закриття 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; }
}
В останньому випадку закриття будується на кожній ітерації циклу, але на відміну від попереднього прикладу воно дешеве, оскільки воно подібне до виклику функції, розподіл купи не проводиться. Більше того, мені цікаво, чи міг компілятор "зняти" закриття та зробити вбудовані оптимізації.
Це правильно? Дякую.
operator()
. Немає жодного "підйому", лямбди не є чимось особливим. Вони просто короткий опис локального об’єкта функції.