2010-05-12 3 views
7

Ich habe eine Funktion, die einen Eingabepuffer von n Bytes erhält und einen Hilfspuffer von n Bytes benötigt, um den angegebenen Eingabepuffer zu verarbeiten.Speicher mit C++ in Echtzeit vormerken

(ich weiß Vektor Speicher zur Laufzeit Zuweisung von, sagen wir mal, dass ich einen Vektor bin mit der statischen vorreservierten Speicher verwendet. Stellen Sie sich dieses ist NICHT ein STL-Vektor.)

Der übliche Ansatz ist

Da ich in einer Echtzeitumgebung arbeite, möchte ich den gesamten Speicher im Voraus reservieren, den ich jemals brauche.

Der Puffer wird nur einmal beim Start zugewiesen. Ich möchte, dass, wenn ich einen Vektor zuzuteilen, ich automatisch Auxiliary-Puffer für meine processData-Funktion zuweisen werde.

ich etwas ähnliches mit einer Template-Funktion mit Vorlagen nicht viel Spaß

static void _processData(vector<T> &vec,vector<T> &aux) { 
    // process data 
} 
template<size_t sz> 
void processData(vector<T> &vec) { 
    static aux_buffer[sz]; 
    vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector 
    _processData(vec,aux); 
} 
// usage: 
processData<V_MAX_SIZE>(v); 

Allerdings arbeiten viel tun kann, ist (jetzt wollen wir alles neu kompilieren, da ich einen Kommentar geändert!), Und es zwingt mich eine Buchhaltung zu tun wann immer ich diese Funktion nutze.

Gibt es schönere Designs für dieses Problem?

+1

Pflicht Frage: Haben Sie profilieren Code, dass die Dynamik zu beweisen Speicherzuordnungen sind wirklich ein Problem? Ich weiß, dass jeder sagt, dass man alles für Echtzeit-Sachen vergeben sollte, aber es hängt wirklich von deinem System ab. –

+2

Abhängig von den Anforderungen habe ich in der Vergangenheit eine vorher festgelegte Größe dynamisch zugewiesen. Vergleichen Sie dann die erforderliche Größe mit der bereits zugewiesenen Größe und weisen Sie sie neu zu, wenn nicht genügend Speicherplatz vorhanden ist. Dadurch wird sichergestellt, dass Sie immer genügend Speicherplatz haben und dass die Zuweisungen letztendlich gestoppt werden, wodurch das System stabilisiert wird. –

+0

@Kristo: "Echtzeit" bedeutet, dass es ein Fehler ist, wenn die Verarbeitung länger als eine bestimmte Zeit dauert. Das Profiling kann nur den schlimmsten Fall anzeigen, der während eines Profillaufs aufgetreten ist, nicht der theoretisch schlimmste Fall. Die dynamische Zuweisung ist nur dann sicher, wenn der Zuordner eine Obergrenze für die benötigte Zeit garantieren kann. –

Antwort

3

Ich sehe nicht, wie Sie genau das bekommen können, was Sie beschreiben. So etwas könnte ein guter Kompromiss für Sie sein.

void processData(vector<T>& vec) 
{ 
    static vector<T> aux(vec.size()); 
    if (vec.size() > aux.size()) { 
     aux.resize(vec.size()); 
    } 
    ... 
} 
+1

Beachten Sie, dass dadurch die Speicherreservierung vom Start bis zum ersten Aufruf verschoben wird - was möglicherweise bereits ein Problem darstellt.Außerdem sind beide Versionen in der Regel nicht Thread-sicher (nicht sicher, ob dies ein Problem für das OP ist) – peterchen

+0

Deshalb habe ich erwähnt, dass es nicht genau das war, was gefragt wurde. Ein Pluspunkt für diese Lösung: Es hat keinen statischen Vektor pro möglicher Größe, was eine Verschwendung sein könnte. – AProgrammer

0

Konnten Sie eine kleine Struktur schaffen, die Ihren Vektor und einen gleich großen Puffer enthielt? Dann würde Ihr Vektor seinen Verarbeitungspuffer mit sich führen, wo immer er hingehört. Wenn Sie es durch Referenz oder Zeiger übergeben, sollten Sie den Kopieraufwand vermeiden. Pseudo-Code folgt unten:

struct Container 
{ 
    vector<T> vec; 
    vector<T> buf; 

    Container(int size) 
    { 
     vec.reserve(size); 
     buf.reserve(size); 
    } 
}; 

Jede Funktion, die derzeit Ihre Vektor-Argument würde dann eine Container nehmen.

1

vector aux(vec.size(),aux_buffer); // use aux_buffer for the vector

Ist das neu in STL? Oder eine benutzerdefinierte Erweiterung?

Die typische Lösung wäre die Verwendung eines benutzerdefinierten Zuordners. Dies ist jedoch nicht unbedingt "schöner" Code.

Some intro slides (Warnung: Powerpoint)
Wikipedia
Google

+0

Selbst zitieren: "(Ich weiß, dass Vektor zur Laufzeit Speicher zuweist, lassen Sie uns sagen, dass ich einen Vektor verwende, der statischen vorbelegten Speicher verwendet. Stellen Sie sich vor, dies ist KEIN STL-Vektor.)". –

1

Auch wenn Sie dies gelingen, kann es nicht erreichen, was Sie wollen. Je nachdem, welches Betriebssystem Sie verwenden und wie virtueller Speicher implementiert wird, erhalten Sie möglicherweise lazy allocation, wobei nur ein Teil Ihrer Speicherzuweisung zugewiesen und initialisiert wird und weitere Seiten aufgrund von Seitenfehlern später zugeordnet werden. Wenn Ihr Betriebssystem mlock oder gleichwertig ist, können Sie dies möglicherweise umgehen.

+0

Seitenfehler? In Echtzeit missionskritisches System? Ich denke nicht. Wo würden wir die Speicherseiten speichern, wenn wir keine Festplatte haben? Der gesamte Code sollte in den Speicher passen und dort bleiben. Oder fehlt mir etwas? –

+0

@Elazar: Sie haben nicht gesagt, welches Betriebssystem Sie verwenden, also könnte ich nur davon ausgehen, dass Sie ein "common or garden" OS wie Linux verwenden. Wenn Sie etwas wie VxWorks (oder zumindest ein Betriebssystem ohne VM) verwenden, ist das natürlich eine andere Geschichte. –

+0

ist Linux Linux der "Common Garden" von Echtzeitsystemen? Ich denke, VxWorks verdient diesen Titel mehr als Linux. –

1

Ich denke, Sie könnten beim Start einen ausreichend großen Speicherpool vormieten und mlock() verwenden und dann normale STL-Container mit Speicherpoolzuweisern (Boost von oder Ihre eigenen) verwenden.

Ich habe das für unsere Echtzeit-Software untersucht, aber Tests haben gezeigt, dass die Speicherzuweisung auf unserer Hardware für unsere Zwecke schnell genug ist.

0

Wahrscheinlich können Sie die neuen und löschenden Operatoren überschreiben, aber dann müssen Sie Ihren gesamten Speicher auf eigene Faust verwalten. Sie können so viel Speicher zuweisen, wie Sie am Anfang wollen:

void* operator new (std::size_t size) throw (std::bad_alloc); 
void* operator new[] (std::size_t size) throw (std::bad_alloc); 
void operator delete (void* ptr) throw(); 
void operator delete[] (void* ptr) throw(); 
1

lassen Sie uns sagen, dass ich einen Vektor bin mit der

statisch vorab zugeordneten Speicher verwendet Dann sollten Sie in der Lage zu bekommen Größe (oder maximale Größe) von vorab zugewiesenem Speicher zur Kompilierungszeit. Wenn ein solcher Vektor seine Größe als Template-Argument hatte, wäre es einfacher, mit der processData-Funktion zu arbeiten.

template<class T, size_t sz> 
class vector 
{ 
enum { size = sz } //either max size 
... 
} 

template<class Vector> 
static void _processData(Vector &vec,Vector &aux) 
{ 
    // process data 
} 
template<class Vector> 
void processData(Vector &vec) { 
    static aux_buffer[Vector::size]; 
    //no need to pass size into constructor, as Vector type knows it already 
    Vector aux(aux_buffer); // use aux_buffer for the vector 
    _processData(vec,aux); 
} 
// usage: 
vector<int, 30> v1; 
vector<float, 40> v2; 
//no need to specify template parameter explicitly 
//every call uses its own function instance and its own buffer of different size 
processData(v1); 
processData(v2); 
1

Es ist nicht das vector Sie sich kümmern müssen, es ist die Speicherzuordner.

Sie könnten perfekt einen Speicherzuordner erstellen, der seinen Speicher vorlistet und ihn dann an den Vektor weitergibt, wenn Sie ihn erstellen, das ist der Vorlagenparameter Alloc!

Und um sicherzustellen, dass der Speicher nicht "virtuell" zugewiesen ist, berühren Sie es, wenn Sie es zuweisen.

scoped_array<byte> buffer = new byte[SIZE]; 
memset(buffer.get(), 0, SIZE); 

Jetzt Sie „nur“ eine benutzerdefinierte allocator implementieren, die auf diesen Speicherpool bezeichnet und an die Vektor-Implementierung passieren :)

+0

Wie würde der benutzerdefinierte Zuordner wissen, wie oft werde ich es nennen? –

+0

Benutzerdefiniert bedeutet, dass Sie es selbst programmieren, Sie können also tun, was Sie wollen, was seine Funktionalität betrifft, wie zum Beispiel das Protokollieren der Zuweisungen und Freigaben für zukünftiges Profiling etc ... –