PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ für Wörterbuch oder NameValuePair-Listen; verschickbar


mekakic
2012-06-25, 11:21:29
Ich versuche gerade etwas zur Thread-, Interprozess- und Rechnerkommunikation zu implementieren, das einem Schlüssel bzw. Namen einen Wert verschiedenen Typs zuordnen und das ganze als verschickbares Binärformat speichert.

Verschickt werden soll eine Liste oder map, in der einem String als Schlüssel ein integraler Wert variablen Typs zugeordnet wird. Also zum Beispiel double, int16, int32, int64, string.

Ich müßte damit einmal über alle Namen iterieren können und diese direkt mit einem Zugriff auch abgefragen können (vermutlich würde bei der zu erwartenden Länge eine eifnach Suche auch nie ein Problem darstellen).

Momentan experimentiere ich mit boost::any und boost::serialization um dies binär und verschickbar ein- und auspacken. Also in Form einer std::map< std::string, boost::any > und einer boost::archive::binary_oarchive. Der less() operator der map ist für boost::any aber etwas hakelig und das boost::serialization Interface ist evtl. ziemlicher Overkill.

Und ich frage mich ob das dafür nicht eine bessere Lösung schon gibt. Ich will einfach eine Liste von Werten mit Namen dran verschicken können. Was macht man da?

Coda
2012-06-25, 11:51:03
Man nimmt sowas wie Google Protocol Buffers, das dafür gemacht wurde ;)

mekakic
2012-06-25, 14:08:19
Danke! Hab da reingeguckt aber ist ProtocolBuffers dafür geeignet, oder wie stark baut das auf ein festes Message Layout?

Es scheint mir momentan da eher Feld-stukturiert zu sein. Ich könnte für meinen Fall im .proto File eine riesige Message definieren und jeden Member als optional deklarieren, aber dann wird doch wenn ich das richtig verstehe trotzdem ein default Wert gespeichert. Wenn ich etwa 200 mögliche NameValueTuple habe, die ich übertragen könnte. Aber je nachdem woher das kommt immer nur 5-10 in einer Nachricht übertragen werden, war meine Idee wirklich nur eine Binaryform einer Liste mit der Zuordnung (Name,Value) zu verschicken. Komplett lose ohne eine feste Nachrichten-Stuktur, vorallem aber mit der Infos: was in der Liste nicht enthalten ist, wurde auch nicht verschickt.

RLZ
2012-06-25, 14:34:14
Als kleiner Denkansatz:

Dieses Adressbuch:

message AddressBook {
repeated Person person = 1;
}

beinhaltet sicher mehr als eine Person. ;)

mekakic
2012-06-25, 15:02:01
Danke, aber versteh ich nicht. Dann hab ich doch bloß wieder eine Liste mit vielen Werten eines Typs? Was ich brauche ist aber eine Liste mit Werten "unterschiedlichen" Typs.

Marscel
2012-06-25, 20:39:11
JSON ist nun nicht binary, aber möglicherweise doch geeignet? Flexibel, Schlüssel->Wert-Struktur, wenig overhead und so ein Interface für (De-)Serialisierung ist schnell designt und Parser existieren ebenso.

mekakic
2012-06-26, 16:10:40
Hört sich zwar interessant aber auch nicht soo performant an. Aber wieviel Performance ich brauche, kann ich momentan noch nicht einschätzen bzw. ob das reicht (s.u.). Ich überlege noch ob ich da ein kleines Format selber mache und mir einfach per Hand meine Name mit Daten und Größe in den Speicher ein- bzw. auspacke.

Ich habe mir jetzt mal doch eine Variante mit boost::serialization und statt boost::any nun boost::variant verwendet, da dies unkompliziert mit dem less Operator ist und ich auch eigentlich nur den Funktionsumfang von boost::variant will. Ich benutze ein boost::archive::binary_iarchive/binary_oarchive und habe alles auf eine std::map oder einen std::vector abgebildet. Der Unterschied zwischen map/vector war aber für diese Operationen (und diesen Füllstand) komplett egal.

Dann habe ich mal nachgemessen, was ich alles zum verpacken und entpacken an Zeit brauche. Um ein Datenpaket/eine Nachricht mit z.B. 10 Tupeln (N,V) zu verpacken, braucht die boost Lösung ~16µs was gefühlt relativ okay ist. Das auspacken ist aus unbekanntem Grund noch deutlich langsamer und braucht etwa ~120µs (beides "optimierter" Release Build). Wobei bei den 120µs fast 100µs allein auf das Konstruieren des boost::archive::binary_iarchive( input_stream& ) fallen (vermutlich wird da aus dem Inputstream kopiert... da muß ich nochmal reinschauen). Der eigentlich Vorgang des de-serialization ist mit 20µs vergleichbar kurz wie das verpacken.

Coda
2012-06-26, 21:23:12
Danke, aber versteh ich nicht. Dann hab ich doch bloß wieder eine Liste mit vielen Werten eines Typs? Was ich brauche ist aber eine Liste mit Werten "unterschiedlichen" Typs.
So ungefähr:

message Value {
extensions 100 to max;

enum Type {
String = 1;
Int = 2;
Float = 3;
}

required Type type = 1;
};

message String {
extend Value {
required String string = 100;
}

required string value = 1;
}

message Int {
extend Value {
required Int int = 101;
}

required int value = 1;
}

message Float {
extend Value {
required Float float = 102;
}

required float value = 1;
}

message KeyValue {
required string key = 1;
required Value value = 2;
}

message Message {
repeated KeyValue data = 1;
}

mekakic
2012-06-27, 18:09:27
Vielen Dank! Ich mußte zwar noch etwas umbauen und nachdem ich bestimmt mich einen halben Tag mit dem Syntax von ProtocolBuffers rumgeschlagen haben (der Zugriff auf die Extensions plus mutable Felder ist etwas umständlich), hab ich es jetzt zum Lauf bekommen. Werde zwar noch eine Facade drum machen, aber die Ergebnisse sehen ganz gut aus.

Da wo die boost/STL Lösung ~16µs für serialize gebraucht hat, braucht die ProtocolBuffers Lösung ~1,4µs. Fürs parse braucht boost/STL Lösung ~120µs und die ProtocolBuffers Lösung ~6,2µs. Die Größe der gleichen Beispielnachricht ist auch kleiner: 225 Byte vs. 133 Byte.

(y)