打包结构/避免填充

Posted

技术标签:

【中文标题】打包结构/避免填充【英文标题】:pack struct / avoid padding 【发布时间】:2015-06-23 07:07:08 【问题描述】:

我有以下结构:

struct SkipListNode
    void        *data;      // 8 bytes
    uint8_t     size;       // 1 byte
    // 7 bytes padding here...
    void        *next[1];   // dynamic array, 8 bytes each "cell"
;

我正在使用malloc(),我分配的空间比sizeof(SkipListNode) 多,所以我正在扩展next[] 数组。

我想避免7 bytes 浪费。我可以完全删除 size 字段,但是我应该在数组末尾保留单个 NULL(8 个字节)。但是,这无助于减小尺寸。

我应该使用__ attribute__((__ packed__)) 还是有其他方法可以解决问题?

这也必须在 C 和 C++ 下编译。

编译器是 gcc。

【问题讨论】:

你考虑过#pragma pack(1) 只是为了确保,打包结构的编译器指令对于不同的编译器是不同的。请同时指定编译器。 @Nick 是正确的,但我会给操作系统一些管理它自己的资源的功劳。当您使用打包时,如果此结构可能的最小尺寸,则为实际尺寸。我相信 malloc 级别中的额外填充不会浪费(想想这样做的后果!),而是被重用于进一步的 malloc 调用。无论如何它很容易测试......只需使用 1000 个结构运行您的进程并查看它消耗了多少内存。最好在编写自己的分配器之前这样做 @Ishay:解压后的结构体中的内存被浪费了。添加它以便变量位于机器字(现在通常为 64 位)的“偶数”边界上。如果你打包这个,它通常用于跨机器边界传输,因为只有这样才知道真实的结构,vars一个接一个地紧挨着。这意味着,当您加载它们时,将会有(afaik)额外的操作来解决它。因此请注意,您正在用内存换取 CPU。只有在真的、真的、真的、必要时才应该打包。 @Lundin:不,它不支持灵活成员,这就是为什么 size 是 1 而不是 0 或只是 []。但是代码工作得很好,你只需要在分配时记住这个 1 。这甚至适用于 POD 类 【参考方案1】:
#include <stdio.h>
#include <stdint.h>

struct SkipListNode
    void        *data;      // 8 bytes
    uint8_t     size;       // 1 byte
    void *next[1];
    ;

struct SkipListNode_pack
    void        *data;      // 8 bytes
    uint8_t     size;       // 1 byte
    void *next[1];
     __attribute__((packed));

int main(int argc, char** argv)

    printf("%d:%d\n", sizeof(struct SkipListNode), sizeof(struct SkipListNode_pack));
    return 0;

以上代码的输出:ishaypeled@arania sandbox]$ ./test24:17

是的,如果你想节省内存,你绝对应该在你的情况下使用__attribute__((packed))。另一方面,就像@MarioTheSpoon 所说的那样 - 它可能会带来性能损失。

我会检查填充解决方案,看看您是否可以在特定机器上忍受这种惩罚。我敢打赌,除非你真的很注重性能,否则你甚至不会注意到它。

【讨论】:

我不同意!仅在确实需要时使用#pragma pack(1)。在标准计算机上的标准程序中浪费约 100k 不应该是最担心的问题。当然,如果它是某种嵌入式系统,或者您要分配数百万条这些记录,则推理可能会有所不同。系统再次欺骗你,因为 malloc 将再次在 64 位边界上对齐缓冲区(在所描述的系统上)。 根据 OP 的 struct 省略 next 成员有什么特别的原因吗? @MarioTheSpoon 这个问题的动机是为了节省内存......我已经同意它可能会导致 CPU 权衡,但这确实是作者要求的......跨度> 这里有一个关于 CPU 惩罚的有趣讨论:***.com/questions/3454673/…【参考方案2】:

我接受了另一个答案,

但是,我确实考虑过重构程序,我找到了一种正确进行的方法,而无需知道数组的大小。

如果有人对此感兴趣,请联系:https://github.com/nmmmnu/HM3/blob/master/skiplist.cc

所以我消除了size 字段并消除了结尾的NULL。现在结构已对齐:)

在重构时,我记得可以使用malloc_usable_size() 来查找已分配块的大小。这是沉重的非便携式黑客,但在其他一些情况下可以很好地工作。

【讨论】:

以上是关于打包结构/避免填充的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核中结构填充/打包的语义是啥?

如何避免结构填充?

如何在数组中打包结构并删除零填充?

为啥这个结构不打包?

Android 安装包优化APK 打包流程 ( 文件结构 | 打包流程 | 安装流程 | 安卓虚拟机 )

Java开发小技巧:Maven多工程依赖项目