2011-10-10 40 views
8

Chúng tôi đang sử dụng protobuf-net để tuần tự hóa và deserialization các thông điệp trong một ứng dụng có giao thức công khai dựa trên Bộ đệm giao thức của Google. Thư viện là tuyệt vời và bao gồm tất cả các yêu cầu của chúng tôi ngoại trừ một trong những yêu cầu này: chúng ta cần phải tìm ra chiều dài tin nhắn được tuần tự hóa theo byte trước khi thông điệp thực sự được tuần tự hóa.nội dung kích thước tin nhắn theo thứ tự protobuf-net

The question đã được hỏi một năm rưỡi trước và theo Marc, cách duy nhất để thực hiện việc này là để tuần tự hóa thành MemoryStream và đọc thuộc tính .Length sau đó. Điều này là không thể chấp nhận được trong trường hợp của chúng ta, bởi vì MemoryStream phân bổ một bộ đệm byte đằng sau hậu trường và chúng ta phải tránh điều này.

Dòng này từ cùng một phản ứng cho chúng ta hy vọng rằng nó có thể là tốt sau khi tất cả:

Nếu bạn làm rõ những gì các use-case là, tôi chắc chắn rằng chúng ta có thể làm cho nó dễ dàng có sẵn (nếu nó chưa được).

Đây là trường hợp sử dụng của chúng tôi. Chúng tôi có các thông báo có kích thước khác nhau giữa nhiều byte và hai megabyte. Ứng dụng phân bổ trước các bộ đệm byte được sử dụng cho các hoạt động ổ cắm và tuần tự hóa/deserializing và khi giai đoạn khởi động kết thúc, không có bộ đệm bổ sung nào có thể được tạo (gợi ý: phân tích GC và phân mảnh đống). Bộ đệm byte cơ bản được gộp chung. Chúng tôi cũng muốn tránh sao chép byte giữa bộ đệm/luồng càng nhiều càng tốt.

Chúng tôi đã đưa ra hai chiến lược tốt và cả hai yêu cầu kích thước tin nhắn trả trước:

  1. Sử dụng (lớn) kích thước cố định byte bộ đệm và serialize tất cả các thư mà có thể phù hợp với một bộ đệm; gửi nội dung của bộ đệm bằng cách sử dụng Socket.Send. Chúng ta phải biết khi nào thông điệp tiếp theo không thể phù hợp với bộ đệm và dừng tuần tự hóa. Nếu không có kích thước tin nhắn, cách duy nhất để đạt được điều này là chờ một ngoại lệ xảy ra trong thời gian Serialize.
  2. Sử dụng bộ đệm byte có kích thước biến nhỏ (nhỏ) và tuần tự hóa từng thông điệp thành một bộ đệm; gửi nội dung của bộ đệm bằng cách sử dụng Socket.Send. Để kiểm tra bộ đệm byte với kích thước thích hợp từ hồ bơi, chúng ta cần phải biết bao nhiêu byte có một thông điệp tuần tự có.

Do giao thức đã được xác định (chúng tôi không thể thay đổi điều này) và yêu cầu tiền tố chiều dài tin nhắn là Varint32, chúng tôi không thể sử dụng phương pháp SerializeWithLengthPrefix.

Vì vậy, có thể thêm phương pháp ước tính kích thước thư mà không cần tuần tự hóa vào luồng? Nếu nó là một cái gì đó mà không phù hợp với các tính năng hiện tại và lộ trình của thư viện, nhưng là doable, chúng tôi đang quan tâm đến việc mở rộng các thư viện chính mình. Chúng tôi cũng đang tìm cách tiếp cận thay thế, nếu có.

Trả lời

4

Như đã lưu ý, điều này không có sẵn ngay lập tức, vì mã cố ý thực hiện một lần vượt qua dữ liệu (đặc biệt là IEnumerable<T> v.v.). Tùy thuộc vào dữ liệu của bạn, tuy nhiên, có thể đã đang thực hiện một số lượng vừa phải sao chép, để cho phép thực tế là các thư phụ là cũng là có độ dài tiền tố, vì vậy có thể cần phải tung hứng. Việc tung hứng này có thể được giảm đáng kể bằng cách sử dụng định dạng phụ "được nhóm" theo số nội bộ trong thông báo vì các nhóm cho phép xây dựng theo hướng về phía trước mà không cần theo dõi.

Vì vậy, có thể thêm phương pháp ước tính kích thước thư mà không cần tuần tự hóa vào luồng?

Ước tính bên cạnh vô dụng; vì không có terminator, nó cần phải chính xác. Cuối cùng, các kích thước có một chút khó dự đoán mà không thực sự thực hiện nó. Đã có một số mã trong v1 cho dự đoán kích thước, nhưng mã một lần hiện có vẻ được ưu tiên và trong hầu hết các trường hợp, chi phí bộ đệm là không đáng kể (có mã để sử dụng lại bộ đệm bên trong để nó không dành tất cả thời gian phân bổ bộ đệm cho các thư nhỏ).

Nếu thông điệp của bạn nội là tiền đạo chỉ (nhóm), sau đó một cheat có thể là để serialize đến một dòng suối giả rằng biện pháp, nhưng giảm tất cả các dữ liệu; bạn sẽ kết thúc serializing hai lần, tuy nhiên.

Re:

và đòi hỏi tiền tố chiều dài thông điệp tới được Varint32, chúng tôi không thể sử dụng SerializeWithLengthPrefix phương pháp

Tôi không khá chắc chắn tôi thấy mối quan hệ đó - nó cho phép một loạt các định dạng vv được sử dụng ở đây; có lẽ nếu bạn có thể cụ thể hơn?

Tái sao chép dữ liệu xung quanh - ý tưởng tôi đã chơi ở đây là sử dụng các biểu mẫu con bình thường cho tiền tố độ dài. Ví dụ, có thể là trong hầu hết các trường hợp, 5 byte là rất nhiều, do đó, thay vì juggle, nó có thể để lại 5 byte và sau đó chỉ cần ghi đè mà không cần ngưng tụ (vì octet 10000000 vẫn có nghĩa là "zero và continue", thậm chí nếu nó dư thừa). Điều này vẫn sẽ cần phải được đệm (để cho phép chèn lấp), nhưng sẽ không yêu cầu và chuyển động của dữ liệu.

Ý tưởng đơn giản cuối cùng sẽ đơn giản: nối tiếp thành FileStream; sau đó ghi độ dài tệp và dữ liệu tệp. Nó giao dịch sử dụng bộ nhớ cho IO, rõ ràng.

+0

Về tiền tố độ dài và SerializeWithLengthPrefix: phương pháp có thể viết tiền tố được mã hóa dưới dạng Base128, Fixed32 và Fixed32BigEndian, nhưng nó không hỗ trợ loại Varint32. Giao thức của chúng tôi xác định cấu trúc thông báo sau: [type: varint32] [length: varint32] [message protobuf thực]. –

+0

@Boris chính xác là những gì base-128 có số trường ** là **. Trường số sẽ là === loại (có thể với một sự thay đổi >> 3). Tôi cần xem * byte chính xác *, nhưng điều đó có thể sử dụng được. Nếu không, chỉ cần thêm kiểu thủ công và mã hóa bằng base-128 và trường 0 (sẽ bỏ qua số trường) –

+0

Cảm ơn bạn đã làm rõ, tôi đoán tôi đã bỏ lỡ điều đó. Và cảm ơn bạn đã phản hồi chi tiết. Chúng tôi vẫn đang đánh giá các phương pháp tiếp cận có thể và cố gắng tìm ra cách tối ưu với việc phân phối dữ liệu và phân bổ bộ đệm ít nhất có thể. –

Các vấn đề liên quan