【问题标题】: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 类

标签: c++ c gcc struct


【解决方案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]$ ./test
24:17

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

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

【讨论】:

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

我接受了另一个答案,

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

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

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

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

【讨论】:

    猜你喜欢
    • 2015-04-05
    • 2021-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    相关资源
    最近更新 更多