【问题标题】:How does Swift store arrays of structs?Swift 如何存储结构数组?
【发布时间】:2014-06-04 12:05:47
【问题描述】:

您能否准确地向 C 程序员解释当您在 Swift 中按照这些思路执行操作时,幕后会发生什么:

struct X { var a: Int32; var b: Int32 }
class Y { var a: Int32; var b: Int32 }
var x1 = X[](count: 1000)
let x2 = X[](count: 1000)
var y1 = Y[](count: 1000)
let y2 = Y[](count: 1000)

具体来说,内存布局到底是什么?什么是从栈中分配的,什么是从堆中分配的?我们在这里分配了多少个单独的内存块?

我的猜测是这样的事情发生了:

  • x1 和 x2 是指向包含数组大小 (1000) 的连续内存块的指针,后跟 2000 个整数(存储 x1[0].a、x1[0].b、x1[1 ].a, ... 按此顺序);内存块是从堆中分配的。

  • y1 和 y2 是指向包含数组大小 (1000) 的连续内存块的指针,后跟 1000 个指针(存储对对象 y1[0]、y1[1]、... );其中每一个都指向一个单独的内存块,该内存块代表对象 Y 的一个实例,这些内存块包含引用计数器 + 字段 a 和 b;每个对象 Y 都是从堆中单独分配的。

这是否与 Apple 当前的 Swift 实现中实际发生的情况相近?

在生成的机器代码中,x1 和 x2 的存储或访问方式有什么区别吗? y1 和 y2 之间呢?

【问题讨论】:

  • 我们不知道,Apple 还没有发布足够的内部信息。
  • @Sulthan:人们可以编译 Swift 代码,在调试器中运行它,反汇编,检查内存布局等。原则上,应该可以回答这个问题,至少对于那些可以访问 Xcode 6 测试版。
  • 但是,考虑到数组具有写时复制行为,因此它们可能只是指向堆上某处真实数组的指针的小包装。包装器(实际数组)可以在堆栈上分配(它是struct)。常量 (let) 数组不能更改大小,而可变 (var) 可以。这可能会对内存布局产生一些影响。可变数组实际上可以在内部使用 NSMutableArray 实例。
  • 这取决于编译器优化,它甚至可能将数组拆分为多个变量,或删除其中的一部分...@Sulthan 数组具有扩展时复制行为,而不是写入时复制。如果您替换数组中的现有元素而不更改其长度,则不会复制该数组。

标签: swift


【解决方案1】:

未指定。您可以去反汇编编译后的代码,但这只是它当前所做的,它可能随时更改,恕不另行通知。

我确实理解 C 程序员想要弄清楚幕后发生的事情的愿望。但你真的真的不应该在乎。就像当 C 语言问世时,过去在汇编中工作的程序员想要确切地知道结构存储在哪个物理内存地址,或者哪些局部变量被映射到 CPU 寄存器以及哪些到堆栈帧。在非常早期的编译器中,您实际上可以知道这一点,但您真的不应该关心。即使您认为这会有所不同,也不会。

【讨论】:

  • 在不知道内存布局的情况下,很难推断内存使用情况(例如,“我可以在 8 GB 的 RAM 中处理多大的数据集”)、内存位置和缓存效率(例如, “数组的线性扫描是否对应于内存的线性扫描”)。
  • 当然。在不确切知道哪些局部变量是寄存器以及哪些在堆栈上的情况下,很难推断效率。这就是为什么旧时代的 ASM 程序员想知道它的原因。同样,如果不确切知道某个结构存储在哪个地址,很难知道您是否可以通过仅保存其地址的低位而不是完整指针来优化空间。这会影响您的内存占用量。
  • 一个 Swift 数组是一种被复制的值类型,但它具有内容共享和大小更改复制语义。它分配了多少内存是未知的。事实上,它在 C 中也是未知的(你真的认为 malloc(x) 完全消耗 x 字节吗?如果是这样,你就被迷惑了。它消耗至少 x 字节,但究竟有多少取决于关于实施)。
  • 您所说的 malloc 是为什么重要的原因之一是知道将 n 个对象的数组分配为单个内存块还是 n+1 个内存块。在前一种情况下,与内存管理相关的开销对于较大的 n 可以忽略不计;在后一种情况下,它变得很重要。这很容易观察到,例如在 Java 中,每个对象都是从堆中分配的,并且每个对象都有一些内存管理开销(比较“整数数组结构”与 Java 中“整数结构数组”的等效项)。跨度>
  • @kelin 是的,也不是。如果您使用 Swift API 访问 OpenGL,则该 API 的实现会处理它。如果您直接访问 OpenGL C 例程,那么参数将是 UnsafePointers 并且您有两个选择:直接操作这些 UnsafePointers 引用的缓冲区或将 Array 的实例作为参数传递,在这种情况下,Swift 会注意将它们转换为连续的 " C 数组”的幕后花絮。
猜你喜欢
  • 1970-01-01
  • 2019-12-22
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多