2011-10-10 13 views
8

Używamy protobuf-net do serializacji i deserializacji wiadomości w aplikacji, której protokół publiczny oparty jest na buforach protokołu Google. Biblioteka jest znakomita i obejmuje wszystkie nasze wymagania, z wyjątkiem tego: musimy dowiedzieć się, jak serializowana jest długość wiadomości w bajtach, zanim wiadomość zostanie serializowana.protobuf-net serializowany rozmiar właściwości

The question już zapytał półtora roku temu i według Marc, jedynym sposobem, aby to zrobić, to do serializacji do MemoryStream i odczytać właściwość .Length później. Jest to niedopuszczalne w naszym przypadku, ponieważ MemoryStream przydziela bufor bajtów za kulisami i musimy tego uniknąć.

Ta linia z tej samej odpowiedzi daje nam nadzieję, że to może być możliwe po wszystkim:

Jeśli wyjaśnienie co use-case jest, jestem pewien, że możemy zrobić to łatwo dostępne (jeśli to jeszcze nie jest).

Oto nasz przypadek użycia. Mamy wiadomości, których rozmiar waha się od kilku bajtów do dwóch megabajtów. Aplikacja wstępnie alokuje bufory bajtów wykorzystywane w operacjach na gniazdach oraz do serializowania/deserializacji, a po fazie rozgrzewania nie można utworzyć żadnych dodatkowych buforów (wskazówka: kodowanie GC i fragmentacja sterty). Bufory bajtów są zasadniczo łączone. Chcemy także unikać kopiowania bajtów między buforami/strumieniami w jak największym stopniu.

Mamy pochodzić z dwóch możliwych strategii i obie z nich wymagają rozmiar wiadomości upfront:

  1. Użyj (duży) o stałej wielkości buforów bajtowych i szeregować wszystkie wiadomości, które można dopasować do jednego bufora; wyślij zawartość bufora przy użyciu Socket.Send. Musimy wiedzieć, kiedy następna wiadomość nie może się zmieścić w buforze i przestać serializować. Bez rozmiaru wiadomości jedynym sposobem, aby to osiągnąć, jest oczekiwanie na wystąpienie wyjątku podczas Serialize.
  2. Użyj (małych) buforów bajtów o zmiennym rozmiarze i serializuj każdą wiadomość w jeden bufor; wyślij zawartość bufora przy użyciu Socket.Send. Aby sprawdzić bufor bajtowy o odpowiednim rozmiarze z puli, musimy wiedzieć, ile bajtów ma zserializowana wiadomość.

Ponieważ protokół jest już zdefiniowany (nie możemy tego zmienić) i wymaga prefiksu długości wiadomości jako Varint32, nie możemy użyć metody SerializeWithLengthPrefix.

Czy jest więc możliwe dodanie metody do oszacowania rozmiaru wiadomości bez serializacji w strumieniu? Jeśli jest to coś, co nie pasuje do obecnego zestawu funkcji i mapy drogowej biblioteki, ale jest wykonalne, jesteśmy zainteresowani samodzielnym rozszerzeniem biblioteki. Szukamy także alternatywnych podejść, jeśli takie istnieją.

Odpowiedz

4

Jak już wspomniano, nie jest to natychmiast dostępne, ponieważ kod celowo próbuje wykonać pojedyncze przejście przez dane (zwłaszcza IEnumerable<T> itp.). W zależności od twoich danych, może to być już robić umiarkowaną ilość kopiowania, aby umożliwić fakt, że podkomunikaty są również długości przedrostka, więc może potrzebować żonglowania. Ten żonglerka może zostać znacznie zmniejszona poprzez użycie "zgrupowanego" pod-formatu wewnętrznie w komunikacie, ponieważ grupy pozwalają na konstrukcję tylko do przodu bez śledzenia.

Czy istnieje możliwość dodania metody, która szacuje rozmiar wiadomości bez serializacji do strumienia?

Prognoza jest obok bezużyteczna; ponieważ nie ma terminatora, musi być dokładny. Ostatecznie, rozmiary są trochę trudne do przewidzenia bez faktycznego wykonania. W v1 był jakiś kod do przewidywania rozmiaru, ale obecnie wydaje się, że kod jednoprzepustowy jest preferowany, a w większości przypadków obciążenie narzutowe ma charakter nominalny (istnieje kod służący do ponownego wykorzystania buforów wewnętrznych, tak aby nie wydawał wszystkich czas przydzielania buforów dla małych wiadomości).

Jeśli wiadomość wewnętrznie jest tylko do przodu (pogrupowane), to oszustwo może być do serializacji do fałszywej strumienia, który środki, ale spada wszystkie dane; skończyłbyś jednak dwa razy serializowanie.

Re:

i wymaga długości wiadomość prefiks być Varint32, nie możemy używać SerializeWithLengthPrefix metoda

Nie jestem pewien, widzę, że związek - umożliwia szeroki zakres formatów itp. do użycia tutaj; może, jeśli możesz być bardziej konkretny?

Ponowne kopiowanie danych - idea, w którą grałem, polega na użyciu podtytułów dla prefiksu długości. Na przykład może być tak, że w większości przypadków 5 bajtów jest dużo, więc zamiast żonglować, może pozostawić 5 bajtów, a następnie po prostu nadpisaćkondensacji (ponieważ oktet 10000000 nadal oznacza "zero i kontynuuj", nawet jeśli jest zbędny). To nadal wymagałoby buforowania (aby umożliwić zasypywanie), ale nie wymagałoby i nie przenosiło danych.

Ostatecznym prostym pomysłem byłoby po prostu: serializować do FileStream; następnie wpisz długość pliku i dane pliku. Oczywiście wymienia zużycie pamięci dla IO.

+0

Odnośnie przedrostka długości i SerializeWithLengthPrefix: metoda może zapisywać prefiksy zakodowane jako Base128, Fixed32 i Fixed32BigEndian, ale nie obsługuje typu Varint32. Nasz protokół definiuje następującą strukturę wiadomości: [type: varint32] [length: varint32] [aktualny komunikat protobuf]. –

+0

@Boris, który jest dokładnie tym, co baza-128 z numerem pola ** jest **. Numer pola będzie === typ (prawdopodobnie z przesunięciem >> 3). Musiałbym zobaczyć * dokładne bajty *, ale jest to prawdopodobnie użyteczne. Jeśli nie, po prostu dodaj typ ręcznie i zakoduj przy pomocy base-128 i pola 0 (które będzie pomijało numer pola). –

+0

Dzięki za wyjaśnienie, myślę, że tęskniłem za tym. I dziękuję za szczegółową odpowiedź. Wciąż analizujemy możliwe podejścia i staramy się znaleźć optymalny sposób przy możliwie jak najmniejszym żonglowaniu danymi i przydzielaniu buforów. –

Powiązane problemy