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

发布于 2024-10-15 10:06:33 字数 497 浏览 9 评论 0原文

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

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

每个结构都不同且大小不同。

我想知道我们可以采取什么优雅的方式来做一些事情:

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

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

更新*** 抱歉没说清楚,缓冲区都是预先分配的。我只需要一种非常快速的方法来获取给定结构类型的正确缓冲区。

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

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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

勿忘初心 2024-10-22 10:06:33
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
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
不必了 2024-10-22 10:06:33
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::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();
}

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

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.

记忆里有你的影子 2024-10-22 10:06:33

您可以编写一个具有数字模板参数和缓冲区静态成员的模板类。像这样的东西(未测试):

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

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

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

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;
与君绝 2024-10-22 10:06:33

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

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

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

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.

牵你手 2024-10-22 10:06:33

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

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

像这样的:

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

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

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

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.

沫雨熙 2024-10-22 10:06:33

这是字段左侧的内容,使用 boost fusion 容器,具体来说,地图.

#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 个)。

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).

柠栀 2024-10-22 10:06:33

尝试使用联合:在编译时解决,您可以在代码中进行少量修改即可使用。

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

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文