Absolute fastest (and hopefully elegant) way to return a certain char buffer given a struct type

社会主义新天地 提交于 2020-01-25 21:25:14

问题


OK first and foremost, performance is most important here so I doubt a map would work. I have a list of structs (about 16 of them) like

struct A { ... };
struct B { ... }; 
...

each are different and each are of different sizes.

I'm wondering what elegant way we might be able to do something along the lines of:

char BufferA[sizeof(struct A)];
char BufferB[sizeof(struct B)];

then write some method or mapping to return BufferA if you are working with struct A. Speed is definitely the most important, I imagine using templates would help but I'm not sure it the whole thing can be templatized.

Update*** Sorry for not being clear, the buffers are all pre-allocated. I just need a very fast way to get the proper Buffer given a struct type.

Update 2*** Sorry for not specifying, alignment is an important trait here and I do in fact byte-align each struct with #pragma pack(push, 1)


回答1:


template<typename X>
struct Buffer
{
    static char *ptr()
    {
        // Note if no alignment is needed for your use then
        // just a simple "static char buf[sizeof(X)]; return buf;"
        // would be sufficient instead of the following.
        union Aligner {
            X x;
            char buf[sizeof(X)];
        };

        static Aligner a;

        return a.buf;
    }
};

struct B
{
    int x, y, z;
};

void foo()
{
    Buffer<B>::ptr()[2] = 12;
}

With g++ -O2 the code above generates just a fixed memory write operation in foo.

.globl _Z3foov
    .type   _Z3foov, @function
_Z3foov:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5

    movb    $12, _ZZN6BufferI1BE3ptrEvE1a+2   <=== this is the assignment

    popl    %ebp
    ret
    .cfi_endproc



回答2:


char BufferA[sizeof(struct A)];

Auto char arrays are not guaranteed to be aligned correctly. (Alignment is guaranteed for operator new(some_size) and new char[some_size], but those are not this case.) However, you can use compiler-specific alignment on a char array.

I imagine using templates would help but I'm not sure it the whole thing can be templatized. … I just need a very fast way to get the proper Buffer given a struct type.

Since this is based on type, a template is the right way to go.

template<class T>
struct Buffer {
  static char buffer[sizeof(T)] __attribute__((aligned));  // gcc's syntax
};

And to access it more conveniently, rather than Buffer<A>::buffer:

template<class T>
inline
char* get_buffer() {
  return Buffer<T>::buffer;
}

void construct_example() {
  new (get_buffer<A>()) A();
  // same as:
  new (Buffer<A>::buffer) A();
}

This only allows one buffer per struct type – and that's likely to be a problem – but it seems to be what you expect and want.




回答3:


You could write a template class having a numerical template parameter, and a static member for the buffer. Something like this (not tested):

template <size_t S>
class GlobalBuffer
   {
   static char Buffer[S];
   };

Getting the buffer for a struct with a specific size can now be written like this:

GlobalBuffer<sizeof(struct A)>::Buffer;



回答4:


If the code that you're calling this from is something that literally has variables of type A or whatever (rather than having some sort of runtime switch), then you can use that as a template parameter when calling the function that returns the buffer. That would look something like this:

template <typename T>
char *makebuffer()
{
  return malloc(sizeof(T));
}

Then, in your code, you write makebuffer<A>() and it will allocate a buffer of the correct size and return it.




回答5:


A suggestion: change in the design. Have the structures return a pointer to their prospective buffers.

According to your post, each structure has-a buffer associated with it. This can be translated as the structure has a method that will return an associated buffer.

Something like this:

struct A
{
  char * get_buffer(void) const;
};

In the implementation file:

static char Buffer_A;  // Static to keep it private to this translation unit.
char * A::get_buffer(void) const
{
  return Buffer_A;
};

This is very efficient as far as execution goes. It also leads to genericity. You could put a pure virtual abstract method in a parent class for returning buffers, and thus deal with pointers or references to the parent class in your other functions.

Note: The implementation above uses an automatic variable declared outside the class. Variables declared inside a structure may be placed on the stack which may have smaller size restriction than a variable declared outside of the class. Larger buffers may also be declared using dynamic memory. See your compiler's documentation for memory capacity restrictions.




回答6:


Here is something left of field, use a boost fusion container, specifically, map.

#include <iostream>
#include <boost/fusion/container/map.hpp>
#include <boost/fusion/sequence/intrinsic/at_key.hpp>
#include <boost/array.hpp>

struct A{int a, b, c;};
struct B{double a, b, c;};
struct C{bool a, b, c; };

template <class T>
struct data
{
  data() : buffer() {}

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

  boost::array<char, sizeof(T)> buffer; // assuming no alignment issues!
};

int main(void)
{
  boost::fusion::map<boost::fusion::pair<A, data<A> >,
                     boost::fusion::pair<B, data<B> >,
                     boost::fusion::pair<C, data<C> >
    > buffer_holder;

  // to access
  std::cout << boost::fusion::at_key<A>(buffer_holder).size() << std::endl; // returns reference to data<A>

  return 0;
}

Here, all the buffers for each type is owned by a single component, and each buffer is properly constructed and can be managed by the data wrapper (useful if you're in a multithreaded environment). Not a static in sight... Downside is that there is a limit to the number of template parameters you can use, you can increase this with a compiler flag (I've used up to 30 before).




回答7:


try to use unions: are resolved at compiling time and you can use with little modifications in your code.

#include <iostream>

typedef struct A_
{
    int kk;
    long long pp;
};

//
// with this kind of struct, you have instant access to A contens without memory malllocs, memory leaks,
// you can use in multithread environments, ......
// you don't need virtual tables and so on. You can inherit.......
// you don't spend more memory yhan really necesary
// you don't spend time in functions calling ...
// 

typedef struct A
{
    union
    {
        A_ a;
        char buffer[sizeof(A_)];
    };
};

int main(int argc, char **argv)
{
    A a;

        // we fill the struct information
    a.a.kk = 12;
    a.a.pp = 99991829;

    A b;

        // we access to A buffer without problems
    memcpy(&b, a.buffer, sizeof(b));
    std::cout << "b.a.kk = " << b.a.kk << " b.a.pp = " << b.a.pp << std::endl;
}


来源:https://stackoverflow.com/questions/4880328/absolute-fastest-and-hopefully-elegant-way-to-return-a-certain-char-buffer-giv

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!