2016-01-16 15 views
5

Rozważmy schemat Cap'n'Proto tak:Stream podczas szeregowania z Cap'n'Proto

struct Document { 
    header @0 : Header; 
    records @1 :List(Record); // usually large number of records. 
    footer @2 :Footer; 
} 
struct Header { numberOfRecords : UInt32; /* some fields */ }; 
struct Footer { /* some fields */ }; 
struct Record { 
    type : UInt32; 
    desc : Text; 
    /* some more fields, relatively large in total */ 
} 

Teraz chcę serializacji (czyli build) wystąpienie dokument i przesyłać go do odległego miejsca przeznaczenia.

Ponieważ dokument jest zwykle bardzo duży, nie chcę go całkowicie skompilować w pamięci przed wysłaniem. Zamiast tego szukam konstruktora, który bezpośrednio wysyła strukturę przez strukturę przez przewód. Taki, że dodatkowy potrzebny bufor pamięci jest stały (tj. O (max (sizeof (Header), sizeof (Record), sizeof (Footer)).)

Patrząc na materiał samouczka, nie znajduję takiego konstruktora. MallocMessageBuilder wydaje się tworzyć wszystko w pamięci pierwszy (wtedy zadzwonić writeMessageToFd na nim).

Czy support Cap'n'Proto API taki use-case?

Albo jest Cap'n'Proto oznaczało więcej do wykorzystania w przypadku wiadomości pasujących do pamięci przed wysłaniem?

W tym przykładzie można pominąć strukturę dokumentu, a następnie włączyć e może po prostu wysłać sekwencję jednej wiadomości nagłówka, n Nagrać wiadomości i jedną stopkę. Ponieważ komunikat Cap'n'Proto sam się ogranicza, powinno to działać. Ale tracisz swój główny dokument - może czasami nie jest to opcja.

Odpowiedz

7

Rozwiązane przez ciebie rozwiązanie - wysyłanie części dokumentu jako oddzielnych wiadomości - jest prawdopodobnie najlepsze dla twojego przypadku użycia. Zasadniczo, Cap'n Proto nie jest przeznaczony do streamowania fragmentów pojedynczej wiadomości, ponieważ nie pasowałoby to do jego właściwości dostępu losowego (np. Co się dzieje, gdy próbujesz podążać za wskaźnikiem, który wskazuje na fragment, którego nie otrzymałeś jeszcze?). Zamiast tego, gdy chcesz strumieniować, powinieneś podzielić dużą wiadomość na serię mniejszych wiadomości.

W przeciwieństwie do innych podobnych systemów (np. Protobuf), Cap'n Proto nie wymaga ściśle komunikatów, aby zmieściły się w pamięci. Konkretnie, możesz zrobić kilka lew, używając mmap(2). Jeśli dane dokumentu pochodzą z pliku na dysku, można mmap() plik do pamięci, a następnie włączyć go do wiadomości. Dzięki mmap() system operacyjny nie odczytuje danych z dysku, dopóki nie spróbujesz uzyskać dostępu do pamięci, a system operacyjny może także wyczyścić strony z pamięci po ich uzyskaniu, ponieważ wie, że nadal ma kopię na dysku. Dzięki temu możesz napisać znacznie prostszy kod, ponieważ nie musisz już myśleć o zarządzaniu pamięcią.

Aby dodać ed mmap() ed do wiadomości Cap'n Proto, należy użyć capnp::Orphanage::referenceExternalData(). Na przykład, biorąc pod uwagę:

struct MyDocument { 
    body @0 :Data; 
    # (other fields) 
} 

Możecie napisać:

// Map file into memory. 
void* ptr = (kj::byte*)mmap(
    nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); 
if (ptr == MAP_FAILED) { 
    KJ_FAIL_SYSCALL("mmap", errno); 
} 
auto data = capnp::Data::Reader((kj::byte*)ptr, size); 

// Incorporate it into a message. 
capnp::MallocMessageBuilder message; 
auto root = message.getRoot<MyDocument>(); 
root.adoptDocumentBody(
    message.getOrphanage().referenceExternalData(data)); 

Ponieważ kapitanie Proto jest zero-copy, to skończyć pisanie pamięć mmap() ed bezpośrednio się do gniazdka bez dostępu kiedykolwiek to. Od systemu operacyjnego zależy odczytywanie zawartości z dysku i do gniazda odpowiednio.

Oczywiście nadal masz problem po stronie odbiorczej. Przekonasz się, że dużo trudniej jest zaprojektować koniec odbiorczy, aby odczytać pamięć. Jedną ze strategii może być zrzucenie całego strumienia bezpośrednio do pliku (bez udziału biblioteki Cap'n Proto), a następnie mmap(), które plik i używają capnp::FlatArrayMessageReader do odczytywania danych lokalnych w postaci mmap().

Opisuję to wszystko, ponieważ jest to zgrabna rzecz, która jest możliwa w Cap'n Proto, ale nie w większości innych frameworków do serializacji (np. Nie można tego zrobić z Protobufem). Granie z użyciem mmap() jest czasem naprawdę przydatne - użyłem tego z powodzeniem w kilku miejscach w projekcie nadrzędnym Cap'n Proto, Sandstorm. Podejrzewam jednak, że dla twojego przypadku użycia, podzielenie dokumentu na szereg wiadomości prawdopodobnie ma więcej sensu.

Powiązane problemy