【问题标题】:WORD Size and C/C++ Variables - Do Smaller Int Sizes Actually Use Less Memory?WORD 大小和 C/C++ 变量 - 较小的 Int 大小实际上使用较少的内存吗?
【发布时间】:2020-08-13 11:20:41
【问题描述】:

我知道这都是特定于实现的,但为了举例,我们假设对于某台现代计算机:

int 占据了整个 WORD

short占了半个WORD

short 实际上会占用更少的内存,还是只存储在 WORD 的前半部分而后半部分未使用内存? C/C++ 编译器是否会尝试将两个或多个较小的变量打包到一个 WORD 中,还是会一直浪费这个空间?

【问题讨论】:

  • 最终取决于每个编译器。但我经常在大至 16 字节的数据段中发现浪费空间——对于单个 charshort。这是因为某些边界上的对齐数据加快了内存访问速度。
  • @usr2564301 我会说它还取决于您如何检测编译器如何编译(优化等......)但是说,通过代码中提供的更多信息,编译器可以生成“更好的”可执行文件
  • 只有在 sizeof(WORD) >= 1 时才会占用更少的内存。这意味着在 char 与 int 大小相同的架构上,char、short 和 int 必须具有相同大小
  • 如果您有动态内存分配(例如,您在 linux 或 windows 等操作系统中运行),那么内存一次分配一页(一次可能是 4k),这增加了另一个未使用空间的级别 - 在您要求的内存量和页面大小之间。将一个简短的单词压缩成半个单词通常会使可执行文件变得更大更慢,因此这是一种权衡。这可能取决于你告诉编译器优化什么

标签: c++ c memory memory-alignment


【解决方案1】:

这很大程度上取决于使用情况。

  1. 您通常可以强制编译器优化空间。
  2. 对象在对齐到其大小的倍数的内存边界时,可以更优化地访问(在大多数体系结构上)。
    因此,编译器可能会注入空间以获得更好的对齐。
    这通常发生在需要将不同大小的对象并排放置时。
  3. 编译器不允许重新排列结构中变量的顺序(如果它们位于同一私有/公共/受保护部分中)。
  4. 我认为本地堆栈帧中的变量排序没有任何要求。因此,编译器应该能够以最佳方式打包局部变量并以最佳方式使用所有可用空间(甚至可能为 POD 变量重新使用空间,或者如果可以将空间保存在寄存器中,则永远不要使用空间)。

但如果你有一个使用相同大小对象的结构。

struct X
{
    short      var1;
    short      var2;
}

那么很可能在上述结构中没有填充(不能保证,但很可能没有填充)。

由于上面的第 3 点:如果您想帮助您的编译器以最佳方式打包一个结构,那么它肯定会让编译器更容易将您的成员从最大到最小排序,因为这使得打包而不需要填充更容易(但是标准对填充没有任何要求)。

// if we assume sizeof(int) == 8
struct Y
{
    char      x;  // 1 byte;
                  // Compiler will (prob) insert 7 bytes of padding here.
                  // to make sure that y is on an 8 byte boundry
                  // for most effecient reads.
    int       y;
    char      z;  // 1 byte
                  // Compiler will (prob) insert 7 bytes of padding here.
                  // to make sure that the whole structure has a size
                  // that is a multiple of 8 (the largest object)
                  // This allows for optimal packing of arrays of type
                  // Y.
};

如果你这样排列对象,编译器仍然可以实现最佳打包和快速访问:

struct Y
{
    int      y;
    char     x;
    char     z;
    // probably add 6 bytes of padding.
    // So that we get optimal access to objects in an array.
};

为了举例,我们假设对于某台现代计算机:

如果我们假设在普通标准架构机器上使用像 clang 或 g++ 这样的现代优秀编译器。即使我们假设没有优化速度。

short 真的会占用更少的内存吗

是的。现代编译器将尽可能多地打包对象,并且可能只使用所需的内存。注意:默认情况下,大多数编译器都会针对速度进行优化,因此将保持最佳的速度对齐方式,因此如果必须的话会填充(如果它们无法重新排序的结构中的对象具有不同的大小)。

还是只存储在 WORD 的前半部分,后半部分未使用内存?

除非有编译器必须维护的某些要求,否则不太可能。类似于结构的顺序。

C/C++ 编译器是否会尝试将两个或多个较小的变量打包到一个 WORD 中

是的。每时每刻。默认值通常是。 1 优化速度。 2 优化大小(并不总是相互排斥)。您还可以强制现代编译器在没有填充的情况下针对空间和包结构进行优化。

还是会一直浪费这个空间?

不太可能。

【讨论】:

  • 很好的答案。我的问题可能不清楚,但我更关心堆栈上的单个变量而不是结构中的变量。您是否也可以添加一些有关此的信息?编译器是否完成了任何类型的堆栈打包,或者 char 和 int 是否会使用相同数量的内存?
  • @JShorthouse 添加关于本地堆栈帧的第 4 点。这可以优化打包,因为变量可以很容易地重新排序。是的,现代(可能非常古老)编译器会将相同大小的变量打包到堆栈帧中以优化空间(只要它们能够保持对齐以获得最佳速度)。
  • 非常感谢您提供有关堆栈的更多信息。我的最后一个问题是:你说现代编译器会尽可能多地打包,但是在优化大小之前,优先考虑速度优化。这两件事不是互相对立的吗?我对 x86 汇编没有很好的了解,但我认为读取打包数据需要额外的指令(即读取整个单词,然后使用位移位/and'ing 来提取所需的位)。不是这样吗?
  • @JShorthouse 读取大小为 2 字节的对象通常只需要一条指令,只要对象与 2 字节边界对齐即可。只要对象与 4 字节边界对齐,读取 4 字节大小的对象只需要一条指令。等。没有必要转移或掩盖。不需要位移位和掩码,因为编译器知道它只使用该部分字,并使用仅对该大小的对象进行操作的适当指令。
  • 附言。 x86 不是现代的(即使与我相比它也很古老)。但我猜即使这个旧指令集也有这种能力,我猜现代指令集更有能力。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 2022-01-17
相关资源
最近更新 更多