serialization of arrays

A

aaragon

Hello everyone,

I've been trying to create a simple way to serialize C++ objects so I
can send messages among different processors (using MPI). Something
very simple is shown below:

template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

typedef typename StorageType::ValueType ValueType;

int size = c.size()*sizeof(ValueType);
char* buffer[size];
memcpy(buffer, &c[0], size);

MPI_Send(&buffer, size, MPI_BYTE, dest, tag, comm);
}

I tested the templated function with arrays of integers and it works
fine. However, when I try to use the dynamic_bitset provided by the
boost library, I cannot pass a reference to c[0] because operator& is
private for that class. Is there another efficient way to do a bitwise
copy???

Thank you all,

 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Hello everyone,

I've been trying to create a simple way to serialize C++ objects so I
can send messages among different processors (using MPI). Something
very simple is shown below:

template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

typedef typename StorageType::ValueType ValueType;

int size = c.size()*sizeof(ValueType);
char* buffer[size];
memcpy(buffer, &c[0], size);

MPI_Send(&buffer, size, MPI_BYTE, dest, tag, comm);
}

I tested the templated function with arrays of integers and it works
fine. However, when I try to use the dynamic_bitset provided by the
boost library, I cannot pass a reference to c[0] because operator& is
private for that class. Is there another efficient way to do a bitwise
copy???

I don't know about dynamic_bitset, but std::bitset has a to_string()
method that converts the content to a string of 1 and 0, if the bitset
is not to large you can still get quite good efficiency from that. There
is also a to_ulong() method to convert it to an unsigned long, but that
only works for small bitsets. I would suspect that dynamic_bitset at
least has a to_string() method.

If small buffers are important you'll have to write code that reads the
value from the bitset and sets the correct bits in a char array, it's
not too much work but just sending a string is a lot easier.
 
G

Gianni Mariani

aaragon said:
Hello everyone,

I've been trying to create a simple way to serialize C++ objects so I
can send messages among different processors (using MPI). Something
very simple is shown below:

template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

why not "const StorageType&" ?
typedef typename StorageType::ValueType ValueType;

int size = c.size()*sizeof(ValueType);
char* buffer[size];
^^^^^^^^^^^^ not sure if this is standard C++ yet.
memcpy(buffer, &c[0], size);

MPI_Send(&buffer, size, MPI_BYTE, dest, tag, comm);

Why not just write it like:

MPI_Send(
reinterpret_cast<const char*>(&c[0]),
size, MPI_BYTE, dest, tag, comm
);

.... although you still don't deal with endian issues.
}

I tested the templated function with arrays of integers and it works
fine. However, when I try to use the dynamic_bitset provided by the
boost library, I cannot pass a reference to c[0] because operator& is
private for that class. Is there another efficient way to do a bitwise
copy???

Sounds like you need to do somthing very different for various different
type.

Usual way to do this is using a "traits" class and specialization.

BTW, you have some serious deficiencies in your serialization as you
have written it. It depends on the receiver to have the same endianness
as the sender.

Here is the beginnings of what I am alluding to.


template <typename T>
struct serialization_traits; // empty class for unknown types.

template <>
struct serialization_traits<int> : serialization_traits_pod<int> {};

template <>
struct serialization_traits<char> : serialization_traits_pod<char> {};

template <typename T>
struct serialization_traits< std::vector<T> > :
serialization_traits_container< std::vector<T> > {};


here, serialization_traits_pod, serialization_traits_container etc will
need to define methods that then allow you to do:


template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

typedef typename serialization_traits<StorageType>::ValueType
ValueType;

typename serialization_traits<StorageType>::Serializer
serializer(c);

MPI_Send(c.buffer(), c.size(), MPI_BYTE, dest, tag, comm);
}
 
J

Jeff F

The boost mpi library does this already. While not in the 1.34.1 released
version it's available from their svn/cvs site.

Jeff Flinn
 
A

aaragon

The boost mpi library does this already. While not in the 1.34.1 released
version it's available from their svn/cvs site.

Jeff Flinn

I could use the boost library in my machine. However, I doubt that the
cluster I will run the parallel program on will have the latest
version of the boost library.
 
A

aaragon

aaragon said:
Hello everyone,
I've been trying to create a simple way to serialize C++ objects so I
can send messages among different processors (using MPI). Something
very simple is shown below:
template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

why not "const StorageType&" ?


typedef typename StorageType::ValueType ValueType;
int size = c.size()*sizeof(ValueType);
char* buffer[size];

^^^^^^^^^^^^ not sure if this is standard C++ yet.
memcpy(buffer, &c[0], size);
MPI_Send(&buffer, size, MPI_BYTE, dest, tag, comm);

Why not just write it like:

MPI_Send(
reinterpret_cast<const char*>(&c[0]),
size, MPI_BYTE, dest, tag, comm
);

... although you still don't deal with endian issues.
I tested the templated function with arrays of integers and it works
fine. However, when I try to use the dynamic_bitset provided by the
boost library, I cannot pass a reference to c[0] because operator& is
private for that class. Is there another efficient way to do a bitwise
copy???

Sounds like you need to do somthing very different for various different
type.

Usual way to do this is using a "traits" class and specialization.

BTW, you have some serious deficiencies in your serialization as you
have written it. It depends on the receiver to have the same endianness
as the sender.

Here is the beginnings of what I am alluding to.

template <typename T>
struct serialization_traits; // empty class for unknown types.

template <>
struct serialization_traits<int> : serialization_traits_pod<int> {};

template <>
struct serialization_traits<char> : serialization_traits_pod<char> {};

template <typename T>
struct serialization_traits< std::vector<T> > :
serialization_traits_container< std::vector<T> > {};

here, serialization_traits_pod, serialization_traits_container etc will
need to define methods that then allow you to do:

template <class StorageType>
void Send(StorageType& c, int dest, int tag, MPI_Comm comm) {

typedef typename serialization_traits<StorageType>::ValueType
ValueType;

typename serialization_traits<StorageType>::Serializer
serializer(c);

MPI_Send(c.buffer(), c.size(), MPI_BYTE, dest, tag, comm);

}

I could create a traits class as you suggested. It is just that I
thought that there was a faster approach to something that looks so
simple. I just needed to create a buffer to pass to the MPI function
but it didn't work with the bitset data structure. I still don't know
how to deal with the bitset since I cannot have a reference pointing
to the first boolean in the bitset so I guess I need to do some kind
of temporary copy anyways.
By the way, the StorageType could be anything, even raw arrays of any
type so functions buffer() and size() don't exist.
 
G

Gianni Mariani

aaragon said:
By the way, the StorageType could be anything, even raw arrays of any
type so functions buffer() and size() don't exist.

I think you need to look at it a little closer. buffer() and size() are
called on an object that is used to convert StorageType to a
serializable buffer and subsequent size.

Also, if StorageType is a float*, what is the size ?
 
G

Gianni Mariani

Gianni said:
I think you need to look at it a little closer. buffer() and size() are
called on an object that is used to convert StorageType to a
serializable buffer and subsequent size.

Also, if StorageType is a float*, what is the size ?

And I need to type it correctly.

typename serialization_traits<StorageType>::Serializer
serializer(c);

MPI_Send(serializer.buffer(), serializer.size(), MPI_BYTE, dest,
tag, comm);
 
A

aaragon

Gianni said:
aaragon wrote:



And I need to type it correctly.

typename serialization_traits<StorageType>::Serializer
serializer(c);

MPI_Send(serializer.buffer(), serializer.size(), MPI_BYTE, dest,
tag, comm);

Ok, got the point, but all this still doesn't solve my original
problem, which was to send a boost::dynamic_bitset using MPI. I will
start a traits class and I guess that I could send it as a string
since the dynamic_bitset supports a function that creates a string
with the boolean values. I'll go back to you guys later. Thanks for
answering.
 
G

Gianni Mariani

aaragon wrote:
....
Ok, got the point, but all this still doesn't solve my original
problem, which was to send a boost::dynamic_bitset using MPI. I will
start a traits class and I guess that I could send it as a string
since the dynamic_bitset supports a function that creates a string
with the boolean values. I'll go back to you guys later. Thanks for
answering.

You will need to "serialize" the bitset which has two components -
number of bits and the bits themselves. Since you can only output
multiples of 8 bits you need to send not only the size in bytes
(serializer.size()), you also need to send the number of significant
bits in the "remainder byte".

e.g.

size=1
bits=01010101
^^^ number of bits in the last byte.
this would be the bit sequence "01010".


ex. 2.

size=0
bytes=

no bits

size=2
bits= 10010100 00001111
^^^ number of bits to use in last byte

would mean: 111110010


class SerializerDynamicBitset
{

std::vector<char> m_data;

enum { bits_per_byte=8, remainder_bits_size=3 };

public:
SerializerDynamicBitset( const boost::dynamic_bitset & i_data )
: m_data(
(i_data.size() + remainder_bits_size + bits_per_byte -1)/
bits_per_byte
)
{
m_data[ 0 ] = i_data.size() % bits_per_byte;

unsigned char l_val = 0x08; // bit location

... now iterate through the bit set popping bits from
... the input and pushing it into the m_data array
}

const char * buffer() const
{
return & m_data[0];
}

const size_t size()
{
return m_data.size();
}
};

.... you'll need a corresponding deserializer function.
 
J

Jeff F

aaragon said:
I could use the boost library in my machine. However, I doubt that the
cluster I will run the parallel program on will have the latest
version of the boost library.

Don't you compile/link on your machine and transmit exe's to the cluster?
I'm not sure why they would need boost installed?

Jeff Flinn
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top