Efficient way of serializing memory in C++

811 views
Skip to first unread message

Stefan Eilemann

unread,
Dec 17, 2014, 5:56:25 AM12/17/14
to flatb...@googlegroups.com
Hi,

I'm using Flatbuffers with ZeroMQ to implement a remote data source for a volume renderer. The bulk of the data are volume bricks, sized in MB to GB. 

Today I'm putting this memory into a 'data:[ubyte];' and serialize it using CreateVector. This popped up as the biggest hot spot in profiling a performance test.

From my understanding, CreateVector processes the elements one-by-one with PushElement. Is there a more efficient way of serialising a chunk of memory?


Cheers,

Stefan.

Gregory Allen

unread,
Dec 17, 2014, 3:26:08 PM12/17/14
to flatb...@googlegroups.com
CreateUninitializedVector(size), and then
dst = GetBufferPointer();

memcpy(dst,src,size); // or however you'd like to copy the chunk of memory

CreateUninitializedVector was my suggestion. I'd love to hear if that helps.

Thanks,
-Greg

Stefan Eilemann

unread,
Dec 17, 2014, 4:01:14 PM12/17/14
to flatb...@googlegroups.com


On Wednesday, December 17, 2014 9:26:08 PM UTC+1, Gregory Allen wrote:
CreateUninitializedVector(size), and then
dst = GetBufferPointer();

memcpy(dst,src,size); // or however you'd like to copy the chunk of memory

Thanks for the hint - sounds promising, but I think I am missing something. My deserialization code receives wrong data when I changed the serialization code as follows:

diff --git livre/Lib/zeq/events.cpp livre/Lib/zeq/events.cpp
index d5aee99..2e6e163 100644
--- livre/Lib/zeq/events.cpp
+++ livre/Lib/zeq/events.cpp
@@ -178,8 +178,10 @@ LODNodeSample deserializeDataSample( const ::zeq::Event& ev
ent )
 {
     ::zeq::Event event( id );
     flatbuffers::FlatBufferBuilder& fbb = event.getFBB();
-    auto data = fbb.CreateVector( memory->getData< uint8_t >(),
-                                  memory->getMemSize( ));
+    const size_t size = memory->getMemSize();
+    auto data = fbb.CreateUninitializedVector< uint8_t >( size );
+    uint8_t* ptr = fbb.GetBufferPointer();
+    ::memcpy( ptr, memory->getData< uint8_t >(), size );

     livrezeq::MemoryBuilder builder( fbb );
     builder.add_data( data ); 

Any ideas? Do you have a working sample or unit test of CreateUnitializedVector?


Cheers,

Stefan.

Gregory Allen

unread,
Dec 17, 2014, 5:58:34 PM12/17/14
to flatb...@googlegroups.com
Your code looks right to me according to my understanding of the new CreateUninitializedVector()

Sorry, I requested the feature (and Wouter added it) but I've yet to actually use it.

The thread is here:https://groups.google.com/forum/#!topic/flatbuffers/wQFC11Y202Q

Stefan Eilemann

unread,
Dec 18, 2014, 3:43:32 AM12/18/14
to flatb...@googlegroups.com
Hi Greg, all,

On 17. Dec 2014, at 23:58, Gregory Allen <gal...@arlut.utexas.edu> wrote:

> Your code looks right to me according to my understanding of the new CreateUninitializedVector()
>
> Sorry, I requested the feature (and Wouter added it) but I've yet to actually use it.

After some debugging it turns out that you can’t use the buffer pointer directly, as it starts with the size of the vector. The correct code is:

const size_t size = memory->getMemSize();
auto data = fbb.CreateUninitializedVector< uint8_t >( size );
uint8_t* ptr = fbb.GetBufferPointer() + sizeof( flatbuffers::uoffset_t );
::memcpy( ptr, memory->getData< uint8_t >(), size );

Bonus points: My throughput jumped from 140 MB/s to 820 MB/s!

Seeing this difference: shouldn’t this code path be the default one in flatbuffers, at least for uint8_t, by using partial template specialization?


Cheers,

Stefan.

--
http://www.eyescale.ch
https://github.com/Eyescale/
http://www.linkedin.com/in/eilemann




signature.asc

Wouter van Oortmerssen

unread,
Jan 5, 2015, 2:52:24 PM1/5/15
to Stefan Eilemann, flatb...@googlegroups.com
Note that the comments for CreateUninitializedVector say:

  // Specialized version for non-copying use cases. Data to be written later.
  // After calling this function, GetBufferPointer() can be cast to the
  // corresponding Vector<> type to write the data (through Data()).

so the correct code was intended to be:

auto ptr = reinterpret_cast<Vector<uint8_t> *>(fbb.GetBufferPointer()).Data();

I guess that isn't the friendliest way, I was merely trying to ensure all use cases are possible.

Template specialization doesn't help with the zero-copy case (i.e. some other bit of code wants to own the copying process).

maybe I should return the ptr through a second argument to make it more obvious?








--
You received this message because you are subscribed to the Google Groups "FlatBuffers" group.
To unsubscribe from this group and stop receiving emails from it, send an email to flatbuffers...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Stefan Eilemann

unread,
Jan 6, 2015, 2:23:42 PM1/6/15
to Wouter van Oortmerssen, flatb...@googlegroups.com

On 5. Jan 2015, at 20:52, Wouter van Oortmerssen <w...@google.com> wrote:

> Note that the comments for CreateUninitializedVector say:
>
> // Specialized version for non-copying use cases. Data to be written later.
> // After calling this function, GetBufferPointer() can be cast to the
> // corresponding Vector<> type to write the data (through Data()).
>
> so the correct code was intended to be:
>
> auto ptr = reinterpret_cast<Vector<uint8_t> *>(fbb.GetBufferPointer()).Data();

Oh dear. Not obvious and mildly scary. I now end up with:

uint8_t* ptr = const_cast< uint8_t* >(
reinterpret_cast< flatbuffers::Vector< const uint8_t >* >(
fbb.GetBufferPointer( ))->Data( ));
::memcpy( ptr, memory->getData< uint8_t >(), size );

This is better in the sense that I won’t need to rely on implementation details, but much worst due to the cast-fest. Can we at least add a non-const Vector::Data()?
signature.asc

Wouter van Oortmerssen

unread,
Jan 7, 2015, 7:00:40 PM1/7/15
to Stefan Eilemann, flatb...@googlegroups.com
Just pushed a commit that makes CreateUninitializedVector return the buffer, which should be easier and less error-prone.
Reply all
Reply to author
Forward
0 new messages