Hello, Complex World

Discussion in 'C++' started by Brian, Jan 12, 2010.

  1. Brian

    Brian Guest

    I've been working on adding support for std::complex to the C++
    Middleware Writer -- http://webEbenezer.net/build_integration.html.

    So far this is what I have:

    template <typename T>
    void
    complexCount(Counter& cntr, std::complex<T> const& cmplx)
    {
    cntr.Add(sizeof(T) * 2);
    }


    template <typename B, typename T>
    void
    complexSend(B* buf, std::complex<T> const& cmplx)
    {
    T real = cmplx.real();
    buf->Receive(&real, sizeof(T));
    T imaginary = cmplx.imag();
    buf->Receive(&imaginary, sizeof(T));
    }


    template <typename B, typename T>
    void
    complexReceive(B* buf, std::complex<T>& cmplx)
    {
    T real, imaginary;
    buf->Give(real);
    cmplx.real(real);
    buf->Give(imaginary);
    cmplx.imag(imaginary);
    }


    Both the send and receive functions are suboptimal due
    to the marshalling being done externally. Is there interest
    in having this common type retrofitted for marshalling?
    To my knowledge the C++ Middleware Writer offers the
    most efficient marshalling approach available today. I
    would like to ask that some accommodations be made
    wrt to the standard so as to allow people to marshall
    instances of this common type more efficiently than
    is possible with the current standard.

    If complex were given a stream constructor, rather
    than needing complexReceive we would have
    something like this:

    template <typename B>
    explicit complex(B* buf)
    {
    buf->Give(real_);
    buf->Give(imag_);
    }

    where real_ and imag_ are the names of the private
    data members of the complex type.

    The C++ Middleware Writer could implement the
    constructor based on reading the complex header,
    but it would be necessary to add a prototype to
    the header --

    template <typename B>
    explicit complex(B*);

    Alternatively it could be --

    template <typename B>
    explicit complex(B&);

    if that were thought to be more appropriate. I'm aware
    of comp.std.c++ and may post something there if there
    is some interest here.


    Brian Wood
    http://webEbenezer.net
    (651) 251-9384

    When a man's ways please the L-RD, He makes
    even his enemies to be at peace with him. Proverbs 16:7
    Brian, Jan 12, 2010
    #1
    1. Advertising

  2. On Jan 12, 3:10 pm, Brian <> wrote:
    > I've been working on adding support for std::complex to the C++
    > Middleware Writer --http://webEbenezer.net/build_integration.html.
    >
    > So far this is what I have:
    >
    > template <typename T>
    > void
    > complexCount(Counter& cntr, std::complex<T> const& cmplx)
    > {
    >   cntr.Add(sizeof(T) * 2);
    >
    > }
    >
    > template <typename B, typename T>
    > void
    > complexSend(B* buf, std::complex<T> const& cmplx)
    > {
    >   T real = cmplx.real();
    >   buf->Receive(&real, sizeof(T));
    >   T imaginary = cmplx.imag();
    >   buf->Receive(&imaginary, sizeof(T));
    >
    > }
    >
    > template <typename B, typename T>
    > void
    > complexReceive(B* buf, std::complex<T>& cmplx)
    > {
    >   T real, imaginary;
    >   buf->Give(real);
    >   cmplx.real(real);
    >   buf->Give(imaginary);
    >   cmplx.imag(imaginary);
    >
    > }
    >
    > Both the send and receive functions are suboptimal due
    > to the marshalling being done externally.  Is there interest
    > in having this common type retrofitted for marshalling?
    > To my knowledge the C++ Middleware Writer offers the
    > most efficient marshalling approach available today.  I
    > would like to ask that some accommodations be made
    > wrt to the standard so as to allow people to marshall
    > instances of this common type more efficiently than
    > is possible with the current standard.
    >
    > If complex were given a stream constructor, rather
    > than needing complexReceive we would have
    > something like this:
    >
    > template <typename B>
    > explicit complex(B* buf)
    > {
    >   buf->Give(real_);
    >   buf->Give(imag_);
    >
    > }
    >
    > where real_ and imag_ are the names of the private
    > data members of the complex type.
    >
    > The C++ Middleware Writer could implement the
    > constructor based on reading the complex header,
    > but it would be necessary to add a prototype to
    > the header --
    >
    > template <typename B>
    > explicit complex(B*);
    >
    > Alternatively it could be --
    >
    > template <typename B>
    > explicit complex(B&);


    It seems that you're using pass-by-pointer everywhere. This is good
    for structs > 8 bytes (or so I've heard for the common platforms), but
    for really small things like ints and doubles, it's much more
    expensive to pass-by-pointer than pass-by-value. Could you add some
    receive-by-value functions and send-by-value functions, co-existing
    with the pass-by-pointer functions to the buf interface? Ex:

    //
    template <typename B, typename T>
    inline void complexReceive(B* buf, std::complex<T>& cmplx)
    {
    cmplx.real(buf->get<T>());
    cmplx.imag(buf->get<T>());
    }
    //

    Assuming POD T types, and decent inline expansion and other compiler
    optimizations, this should compile down to basically what you want. Do
    you disagree? Have you actually measured otherwise?

    Are you unable to modify the buf interface to have a get<T> call?

    Are you concerned about std::complex<T> when T is not POD?
    std::complex's interface is expressly designed for small objects and
    pass-by-value. If someone is using it with some other type, then I
    would say they're doing it wrong, and std::complex is the correct
    choice in their situation. They'll have more speed problems than just
    their serialization.
    Joshua Maurice, Jan 14, 2010
    #2
    1. Advertising

  3. In message
    <>,
    Joshua Maurice <> writes

    >
    >Are you concerned about std::complex<T> when T is not POD?


    That shouldn't be a consideration. The behaviour of std::complex<T> is
    unspecified for values of T other than float, double and long double
    [26.2/2]

    >std::complex's interface is expressly designed for small objects and
    >pass-by-value. If someone is using it with some other type, then I
    >would say they're doing it wrong, and std::complex is the correct
    >choice in their situation. They'll have more speed problems than just
    >their serialization.


    They'll have more than speed problems.

    --
    Richard Herring
    Richard Herring, Jan 14, 2010
    #3
  4. Brian

    Brian Guest

    On Jan 14, 12:01 am, Joshua Maurice <> wrote:
    > On Jan 12, 3:10 pm, Brian <> wrote:
    >
    >
    >
    > > I've been working on adding support for std::complex to the C++
    > > Middleware Writer --http://webEbenezer.net/build_integration.html.

    >
    > > So far this is what I have:

    >
    > > template <typename T>
    > > void
    > > complexCount(Counter& cntr, std::complex<T> const& cmplx)
    > > {
    > >   cntr.Add(sizeof(T) * 2);

    >
    > > }

    >
    > > template <typename B, typename T>
    > > void
    > > complexSend(B* buf, std::complex<T> const& cmplx)
    > > {
    > >   T real = cmplx.real();
    > >   buf->Receive(&real, sizeof(T));
    > >   T imaginary = cmplx.imag();
    > >   buf->Receive(&imaginary, sizeof(T));

    >
    > > }

    >
    > > template <typename B, typename T>
    > > void
    > > complexReceive(B* buf, std::complex<T>& cmplx)
    > > {
    > >   T real, imaginary;
    > >   buf->Give(real);
    > >   cmplx.real(real);
    > >   buf->Give(imaginary);
    > >   cmplx.imag(imaginary);

    >
    > > }

    >
    > > Both the send and receive functions are suboptimal due
    > > to the marshalling being done externally.  Is there interest
    > > in having this common type retrofitted for marshalling?
    > > To my knowledge the C++ Middleware Writer offers the
    > > most efficient marshalling approach available today.  I
    > > would like to ask that some accommodations be made
    > > wrt to the standard so as to allow people to marshall
    > > instances of this common type more efficiently than
    > > is possible with the current standard.

    >
    > > If complex were given a stream constructor, rather
    > > than needing complexReceive we would have
    > > something like this:

    >
    > > template <typename B>
    > > explicit complex(B* buf)
    > > {
    > >   buf->Give(real_);
    > >   buf->Give(imag_);

    >
    > > }

    >
    > > where real_ and imag_ are the names of the private
    > > data members of the complex type.

    >
    > > The C++ Middleware Writer could implement the
    > > constructor based on reading the complex header,
    > > but it would be necessary to add a prototype to
    > > the header --

    >
    > > template <typename B>
    > > explicit complex(B*);

    >
    > > Alternatively it could be --

    >
    > > template <typename B>
    > > explicit complex(B&);

    >
    > It seems that you're using pass-by-pointer everywhere. This is good
    > for structs > 8 bytes (or so I've heard for the common platforms), but
    > for really small things like ints and doubles, it's much more
    > expensive to pass-by-pointer than pass-by-value. Could you add some
    > receive-by-value functions and send-by-value functions, co-existing
    > with the pass-by-pointer functions to the buf interface? Ex:
    >


    Possibly.


    > //
    > template <typename B, typename T>
    > inline void complexReceive(B* buf, std::complex<T>& cmplx)
    > {
    >   cmplx.real(buf->get<T>());
    >   cmplx.imag(buf->get<T>());}
    >
    > //
    >
    > Assuming POD T types, and decent inline expansion and other compiler
    > optimizations, this should compile down to basically what you want.


    I'm not sure if that would work out as well.
    Assuming a Receive by value function like this:

    template <typename T>
    Receive(T const val)
    {
    Receive(&val, sizeof(T));
    }

    and then

    template <typename B, typename T>
    void
    complexSend(B* buf, std::complex<T> const& cmplx)
    {
    buf->Receive(cmplx.real());
    buf->Receive(cmplx.imag());
    }


    I don't know if that's as good as an internal
    implementation:

    template <typename B, typename T>
    void
    complex<T>::Send(B* buf)
    {
    buf->Receive(&real_, sizeof(T);
    buf->Receive(&imag_, sizeof(T));
    }


    It seems to me that the former would require copies
    of the floats/doubles/long doubles/ that the latter
    would not.

    It might not be advantageous to have an internal
    implementation for sending since I think it is safe,
    with the way I'm doing things byte order-wise, to
    send the data with something like:

    complex<float> c;
    buf->Receive(&c, sizeof(c));

    But when receiving a std::complex object I have to,
    at least in some circumstances, handle each field
    separately. If both machines involved have the
    same endianess, you can copy a group of complex
    instances, but if the two machines aren't the same
    you have to use something like the complexReceive
    function that you and I have written.


    >Have you actually measured otherwise?


    No.

    >
    > Are you unable to modify the buf interface to have a get<T> call?


    That's a decent idea in general, but am not sure that it
    offers a solution that's as good as doing the marshalling
    internally.


    Brian Wood
    http://webEbenezer.net
    (651) 251-9384
    Brian, Jan 14, 2010
    #4
  5. On Jan 14, 2:55 pm, Brian <> wrote:
    > On Jan 14, 12:01 am, Joshua Maurice <> wrote:
    > > On Jan 12, 3:10 pm, Brian <> wrote:
    > > > I've been working on adding support for std::complex to the C++
    > > > Middleware Writer --http://webEbenezer.net/build_integration.html.

    >
    > > > So far this is what I have:

    >
    > > > template <typename T>
    > > > void
    > > > complexCount(Counter& cntr, std::complex<T> const& cmplx)
    > > > {
    > > > cntr.Add(sizeof(T) * 2);

    >
    > > > }

    >
    > > > template <typename B, typename T>
    > > > void
    > > > complexSend(B* buf, std::complex<T> const& cmplx)
    > > > {
    > > > T real = cmplx.real();
    > > > buf->Receive(&real, sizeof(T));
    > > > T imaginary = cmplx.imag();
    > > > buf->Receive(&imaginary, sizeof(T));

    >
    > > > }

    >
    > > > template <typename B, typename T>
    > > > void
    > > > complexReceive(B* buf, std::complex<T>& cmplx)
    > > > {
    > > > T real, imaginary;
    > > > buf->Give(real);
    > > > cmplx.real(real);
    > > > buf->Give(imaginary);
    > > > cmplx.imag(imaginary);

    >
    > > > }

    >
    > > > Both the send and receive functions are suboptimal due
    > > > to the marshalling being done externally. Is there interest
    > > > in having this common type retrofitted for marshalling?
    > > > To my knowledge the C++ Middleware Writer offers the
    > > > most efficient marshalling approach available today. I
    > > > would like to ask that some accommodations be made
    > > > wrt to the standard so as to allow people to marshall
    > > > instances of this common type more efficiently than
    > > > is possible with the current standard.

    >
    > > > If complex were given a stream constructor, rather
    > > > than needing complexReceive we would have
    > > > something like this:

    >
    > > > template <typename B>
    > > > explicit complex(B* buf)
    > > > {
    > > > buf->Give(real_);
    > > > buf->Give(imag_);

    >
    > > > }

    >
    > > > where real_ and imag_ are the names of the private
    > > > data members of the complex type.

    >
    > > > The C++ Middleware Writer could implement the
    > > > constructor based on reading the complex header,
    > > > but it would be necessary to add a prototype to
    > > > the header --

    >
    > > > template <typename B>
    > > > explicit complex(B*);

    >
    > > > Alternatively it could be --

    >
    > > > template <typename B>
    > > > explicit complex(B&);

    >
    > > It seems that you're using pass-by-pointer everywhere. This is good
    > > for structs > 8 bytes (or so I've heard for the common platforms), but
    > > for really small things like ints and doubles, it's much more
    > > expensive to pass-by-pointer than pass-by-value. Could you add some
    > > receive-by-value functions and send-by-value functions, co-existing
    > > with the pass-by-pointer functions to the buf interface? Ex:

    >
    > Possibly.
    >
    > > //
    > > template <typename B, typename T>
    > > inline void complexReceive(B* buf, std::complex<T>& cmplx)
    > > {
    > > cmplx.real(buf->get<T>());
    > > cmplx.imag(buf->get<T>());}

    >
    > > //

    >
    > > Assuming POD T types, and decent inline expansion and other compiler
    > > optimizations, this should compile down to basically what you want.

    >
    > I'm not sure if that would work out as well.
    > Assuming a Receive by value function like this:
    >
    > template <typename T>
    > Receive(T const val)
    > {
    > Receive(&val, sizeof(T));
    >
    > }


    Stop right here. What are you doing? You take the address of a const
    variable (val) and try to modify it with Receive. Undefined behavior.
    You are also trying to modify a parameter (val) which will cease to
    exist and not affect the caller's argument in any way.

    I was just thinking about my earlier suggestion. I think some of the
    general sentiment is true, but I'd much rather actually code and test
    a lot of it. I should check an actual compiler at some point in the
    near future.

    Also, I was thinking that you might be wrong in the first post when
    you called the following less than optimal:

    template <typename B, typename T>
    void complexReceive(B* buf, std::complex<T>& cmplx)
    {
    T real, imaginary;
    buf->Give(real);
    cmplx.real(real);
    buf->Give(imaginary);
    cmplx.imag(imaginary);
    }

    The compiler could expand inline to make the following:

    template <typename B, typename T>
    void complexReceive(B* buf, std::complex<T>& cmplx)
    {
    T real;
    buf->Give(real);
    cmplx.real_ = real;

    T imaginary;
    buf->Give(imaginary);
    cmplx.imag_ = imaginary;
    }

    The compiler the might be able to do flow analysis and detect that the
    stack objects "real" and "imaginary" are useless intermediaries, and
    reduce it to:

    template <typename B, typename T>
    void complexReceive(B* buf, std::complex<T>& cmplx)
    {
    buf->Give(cmplx.real_);
    buf->Give(cmplx.imag_);
    }

    So, I again ask you sir: have you actually measured any of this? (Note
    that most of my arguments are in the context of very small POD
    objects, like T will be for std::complex, and I have been assuming
    decent levels of optimization.)

    In your case, the compiler has access to buf->Give as it's a template
    method (as presumably your compiler does not support "template
    extern"). It may or may not be able to do that flow analysis to
    determine that the objects are useless intermediaries. Perhaps buf-
    >Give is above the compiler's threshold for expanding inline and also

    bigger than its threshold for doing this flow analysis. Then you will
    have something like an extra load-from-memory and store-to-memory
    assembly instruction for each member of std::complex. That is why I
    suggested a parallel interface which returns by value. This would not
    so easily defeat the compiler's flow analysis, and better
    optimizations could be done. Ex:

    template <typename B, typename T>
    void complexReceive(B* buf, std::complex<T>& cmplx)
    {
    cmplx.real(buf->get<T>());
    cmplx.imag(buf->get<T>());
    }

    Simply making buf::get a wrapper over buf::Give, like you did in your
    followup post, would not improve performance. It just moves the
    location of the questionable flow analysis somewhere else. I was
    suggesting something fundamentally different. Instead of having a
    framework which passes by pointer everywhere, throughout the entire
    buffer interface and implementations provide a parallel interface
    which returns by value instead of returns by pointer-parameter. This
    would remove the extra copy. I recognize it's probably not an easy
    change, but I would expect that when used properly, it would speed up
    the code and make it somewhat shorter and definitely cleaner. However,
    I would again measure.
    Joshua Maurice, Jan 15, 2010
    #5
  6. Brian

    Brian Guest

    On Jan 14, 7:30 pm, Joshua Maurice <> wrote:
    > On Jan 14, 2:55 pm, Brian <> wrote:
    >
    >
    >
    > > On Jan 14, 12:01 am, Joshua Maurice <> wrote:
    > > > On Jan 12, 3:10 pm, Brian <> wrote:
    > > > > I've been working on adding support for std::complex to the C++
    > > > > Middleware Writer --http://webEbenezer.net/build_integration.html.

    >
    > > > > So far this is what I have:

    >
    > > > > template <typename T>
    > > > > void
    > > > > complexCount(Counter& cntr, std::complex<T> const& cmplx)
    > > > > {
    > > > >   cntr.Add(sizeof(T) * 2);

    >
    > > > > }

    >
    > > > > template <typename B, typename T>
    > > > > void
    > > > > complexSend(B* buf, std::complex<T> const& cmplx)
    > > > > {
    > > > >   T real = cmplx.real();
    > > > >   buf->Receive(&real, sizeof(T));
    > > > >   T imaginary = cmplx.imag();
    > > > >   buf->Receive(&imaginary, sizeof(T));

    >
    > > > > }

    >
    > > > > template <typename B, typename T>
    > > > > void
    > > > > complexReceive(B* buf, std::complex<T>& cmplx)
    > > > > {
    > > > >   T real, imaginary;
    > > > >   buf->Give(real);
    > > > >   cmplx.real(real);
    > > > >   buf->Give(imaginary);
    > > > >   cmplx.imag(imaginary);

    >
    > > > > }

    >
    > > > > Both the send and receive functions are suboptimal due
    > > > > to the marshalling being done externally.  Is there interest
    > > > > in having this common type retrofitted for marshalling?
    > > > > To my knowledge the C++ Middleware Writer offers the
    > > > > most efficient marshalling approach available today.  I
    > > > > would like to ask that some accommodations be made
    > > > > wrt to the standard so as to allow people to marshall
    > > > > instances of this common type more efficiently than
    > > > > is possible with the current standard.

    >
    > > > > If complex were given a stream constructor, rather
    > > > > than needing complexReceive we would have
    > > > > something like this:

    >
    > > > > template <typename B>
    > > > > explicit complex(B* buf)
    > > > > {
    > > > >   buf->Give(real_);
    > > > >   buf->Give(imag_);

    >
    > > > > }

    >
    > > > > where real_ and imag_ are the names of the private
    > > > > data members of the complex type.

    >
    > > > > The C++ Middleware Writer could implement the
    > > > > constructor based on reading the complex header,
    > > > > but it would be necessary to add a prototype to
    > > > > the header --

    >
    > > > > template <typename B>
    > > > > explicit complex(B*);

    >
    > > > > Alternatively it could be --

    >
    > > > > template <typename B>
    > > > > explicit complex(B&);

    >
    > > > It seems that you're using pass-by-pointer everywhere. This is good
    > > > for structs > 8 bytes (or so I've heard for the common platforms), but
    > > > for really small things like ints and doubles, it's much more
    > > > expensive to pass-by-pointer than pass-by-value. Could you add some
    > > > receive-by-value functions and send-by-value functions, co-existing
    > > > with the pass-by-pointer functions to the buf interface? Ex:

    >
    > > Possibly.

    >
    > > > //
    > > > template <typename B, typename T>
    > > > inline void complexReceive(B* buf, std::complex<T>& cmplx)
    > > > {
    > > >   cmplx.real(buf->get<T>());
    > > >   cmplx.imag(buf->get<T>());}

    >
    > > > //

    >
    > > > Assuming POD T types, and decent inline expansion and other compiler
    > > > optimizations, this should compile down to basically what you want.

    >
    > > I'm not sure if that would work out as well.
    > > Assuming a Receive by value function like this:

    >
    > > template <typename T>
    > > Receive(T const val)
    > > {
    > >   Receive(&val, sizeof(T));

    >
    > > }

    >
    > Stop right here. What are you doing? You take the address of a const
    > variable (val) and try to modify it with Receive. Undefined behavior.
    > You are also trying to modify a parameter (val) which will cease to
    > exist and not affect the caller's argument in any way.
    >


    Receive doesn't modify anything. Sorry if it was confusing,
    but I switched from focusing on complexReceive to complexSend.


    > I was just thinking about my earlier suggestion. I think some of the
    > general sentiment is true, but I'd much rather actually code and test
    > a lot of it. I should check an actual compiler at some point in the
    > near future.
    >
    > Also, I was thinking that you might be wrong in the first post when
    > you called the following less than optimal:
    >
    > template <typename B, typename T>
    > void complexReceive(B* buf, std::complex<T>& cmplx)
    > {
    >   T real, imaginary;
    >   buf->Give(real);
    >   cmplx.real(real);
    >   buf->Give(imaginary);
    >   cmplx.imag(imaginary);
    >
    > }
    >
    > The compiler could expand inline to make the following:
    >
    > template <typename B, typename T>
    > void complexReceive(B* buf, std::complex<T>& cmplx)
    > {
    >   T real;
    >   buf->Give(real);
    >   cmplx.real_ = real;
    >
    >   T imaginary;
    >   buf->Give(imaginary);
    >   cmplx.imag_ = imaginary;
    >
    > }
    >
    > The compiler the might be able to do flow analysis and detect that the
    > stack objects "real" and "imaginary" are useless intermediaries, and
    > reduce it to:
    >
    > template <typename B, typename T>
    > void complexReceive(B* buf, std::complex<T>& cmplx)
    > {
    >   buf->Give(cmplx.real_);
    >   buf->Give(cmplx.imag_);
    >
    > }
    >
    > So, I again ask you sir: have you actually measured any of this? (Note
    > that most of my arguments are in the context of very small POD
    > objects, like T will be for std::complex, and I have been assuming
    > decent levels of optimization.)


    I'm fine with the above and think your comment here is
    about right. I'm not sure if some or most compilers
    would be able to make these optimizations, but am of the
    opinion that handling the marshalling internally would
    work equally well with "smarter" compilers as it would
    with simpler compilers/less optimization.


    Brian Wood
    http://webEbenezer.net
    Brian, Jan 15, 2010
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Carl
    Replies:
    4
    Views:
    403
    Peter Hansen
    May 21, 2004
  2. Daniel Crespo
    Replies:
    5
    Views:
    393
    Amaury
    Nov 13, 2005
  3. vijay
    Replies:
    8
    Views:
    689
  4. Roy
    Replies:
    6
    Views:
    578
    Roedy Green
    Jan 7, 2008
  5. Larry
    Replies:
    27
    Views:
    408
    Michele Dondi
    Jan 25, 2005
Loading...

Share This Page