给定结构类型的绝对最快(并且希望是优雅)方式返回某个char缓冲区

时间:2011-02-02 21:55:48

标签: c++ templates struct mapping low-latency

好的,首先,性能在这里是最重要的,所以我怀疑地图是否有效。我有一个结构列表(大约16个),如

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

每个都不同,每个都有不同的大小。

我想知道我们可以采取哪种优雅的方式:

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

然后编写一些方法或映射来返回BufferA如果你正在使用struct A.速度绝对是最重要的,我想使用模板会有所帮助,但我不确定它是否整个事情都可以被模板化。

更新***对不清楚,缓冲区都已预先分配。我只需要一种非常快速的方法来获得给定结构类型的正确缓冲区。

更新2 ***很抱歉没有指定,对齐是一个重要的特性,我实际上用#pragma pack(push,1)对每个结构进行字节对齐

7 个答案:

答案 0 :(得分:4)

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;
}

使用g++ -O2,上面的代码只会在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

答案 1 :(得分:1)

char BufferA[sizeof(struct A)];

无法保证自动排列数组正确对齐。 (运算符new(some_size)和new char [some_size]保证对齐,但不是这种情况。)但是,您可以在char数组上使用特定于编译器的对齐。

  

我想使用模板会有所帮助,但我不确定整个事情是否可以模板化。 ...我只需要一种非常快速的方法来获得给定结构类型的正确缓冲区。

由于这是基于类型的,因此模板是正确的方法。

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

更方便地访问它,而不是Buffer&lt; A&gt; :: 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();
}

这只允许每个结构类型一个缓冲区 - 这可能是一个问题 - 但它似乎是你期望和想要的。

答案 2 :(得分:0)

您可以编写具有数字模板参数的模板类,以及缓冲区的静态成员。这样的事情(未经测试):

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

获取具有特定大小的结构的缓冲区现在可以这样写:

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

答案 3 :(得分:0)

如果您从中调用此代码的代码实际上具有类型A或其他类型的变量(而不是某种运行时切换),那么您可以在调用时将其用作模板参数返回缓冲区的函数。这看起来像这样:

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

然后,在您的代码中,您编写makebuffer<A>(),它将分配一个正确大小的缓冲区并将其返回。

答案 4 :(得分:0)

建议:改变设计。让结构返回指向其预期缓冲区的指针。

根据您的帖子,每个结构都有一个与之关联的缓冲区。这可以翻译为该结构具有将返回相关缓冲区的方法

这样的事情:

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

在实施文件中:

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

就执行而言,这是非常有效的。它也导致了通用性。您可以将纯虚拟抽象方法放在父类中以返回缓冲区,从而处理指向其他函数中父类的指针或引用。

注意:上面的实现使用在类外声明的自动变量。在结构内声明的变量可以放在堆栈上,它可能比在类外部声明的变量具有更小的限制。也可以使用动态内存声明更大的缓冲区。有关内存容量限制,请参阅编译器的文档。

答案 5 :(得分:0)

这是字段剩下的东西,使用boost融合容器,特别是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;
}

这里,每种类型的所有缓冲区都由一个组件拥有,并且每个缓冲区都是正确构造的,并且可以由data包装器进行管理(如果您处于多线程环境中,则非常有用)。看起来不是静止...下行是你可以使用的模板参数数量有限,你可以用编译器标志来增加它(之前我用了30个)。

答案 6 :(得分:0)

尝试使用联合:在编译时解析,您可以在代码中稍作修改即可使用。

#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;
}