Vorrei alcune informazioni su come pensare correttamente alle chiusure C ++ 11 e std::function
in termini di come vengono implementate e come viene gestita la memoria.
Anche se non credo nell'ottimizzazione prematura, ho l'abitudine di considerare attentamente l'impatto sulle prestazioni delle mie scelte durante la scrittura di nuovo codice. Faccio anche una discreta quantità di programmazione in tempo reale, ad esempio su microcontrollori e sistemi audio, dove si devono evitare pause di allocazione / deallocazione della memoria non deterministiche.
Pertanto, mi piacerebbe sviluppare una migliore comprensione di quando utilizzare o meno i lambda C ++.
La mia comprensione attuale è che un lambda senza chiusura acquisita è esattamente come un callback C. Tuttavia, quando l'ambiente viene acquisito in base al valore o al riferimento, viene creato un oggetto anonimo nello stack. Quando una chiusura di valore deve essere restituita da una funzione, la si avvolge std::function
. Cosa succede alla memoria di chiusura in questo caso? È stato copiato dallo stack all'heap? Viene liberato ogni volta che std::function
viene liberato, ovvero viene contato come riferimento come a std::shared_ptr
?
Immagino che in un sistema in tempo reale potrei impostare una catena di funzioni lambda, passando B come argomento di continuazione ad A, in modo che A->B
venga creata una pipeline di elaborazione . In questo caso, le chiusure A e B sarebbero assegnate una volta. Anche se non sono sicuro se questi sarebbero allocati sullo stack o sull'heap. Tuttavia, in generale, questo sembra sicuro da usare in un sistema in tempo reale. D'altra parte se B costruisce una funzione lambda C, che restituisce, la memoria per C verrebbe allocata e deallocata ripetutamente, il che non sarebbe accettabile per l'utilizzo in tempo reale.
In pseudo-codice, un loop DSP, che penso sarà sicuro in tempo reale. Voglio eseguire il blocco di elaborazione A e poi B, dove A chiama il suo argomento. Entrambe queste funzioni restituiscono std::function
oggetti, quindi f
sarà un std::function
oggetto, dove il suo ambiente è memorizzato nell'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)
}
E uno che penso potrebbe essere cattivo da usare nel codice in tempo reale:
for (t=0; t<1000; t++) {
y = A(B)(t);
}
E uno in cui penso che la memoria dello stack sia probabilmente utilizzata per la chiusura:
freq = 220;
A = 2;
for (t=0; t<1000; t++) {
y = [=](int t){ return sin(t*freq)*A; }
}
In quest'ultimo caso la chiusura viene costruita ad ogni iterazione del ciclo, ma a differenza dell'esempio precedente è economica perché è proprio come una chiamata di funzione, non vengono effettuate allocazioni di heap. Inoltre, mi chiedo se un compilatore potrebbe "sollevare" la chiusura e fare ottimizzazioni in linea.
È corretto? Grazie.
operator()
. Non c'è "sollevamento" da fare, i lambda non sono niente di speciale. Sono solo una scorciatoia per un oggetto funzione locale.