5-5.【性能分析与优化】在什么情况下 struct 的性能会比 class 更差?请举真实场景。

2 阅读2分钟

当“值语义的复制成本”
大于“引用语义的 ARC 成本”时,
struct 会比 class 慢,而且可能慢很多。

下面我用 真实可遇到的场景 给你拆。


一、真实场景 1:大 struct + 频繁传参(最常见)

❌ struct 翻车版本

struct Packet {
    var header: Header
    var payload: [UInt8]   // 很大
    var checksum: Int
}

func process(_ p: Packet) {
    // 只读
}

for packet in packets {
    process(packet)
}

为什么慢?

  • Packet 是值类型

  • 每次 process(packet) 都是 值传递

  • payload 是大数组 → CoW

  • 一旦 process 或更深层出现写操作:

    • 整个 payload 被复制

哪怕你“以为”只读。


✅ class 版本更快

final class Packet {
    var header: Header
    var payload: [UInt8]
    var checksum: Int
}
  • 只传一个指针
  • 没有 CoW
  • ARC 成本 < 大内存复制

👉 在“大对象 + 深调用栈”中,class 胜


二、真实场景 2:struct 在集合中频繁修改

❌ struct in Array

struct Node {
    var x: Int
    var y: Int
    var data: [Float]  // 非常大
}

var nodes = loadMillionNodes()

for i in nodes.indices {
    nodes[i].x += 1
}

问题

  • nodes 是 Array(本身 CoW)

  • Node 是 struct

  • 修改一个字段:

    • 整个 Node 被 copy
    • 内部 data 触发 CoW

👉 你只改了 x,却搬了整栋楼


class 版本

final class Node {
    var x: Int
    var y: Int
    var data: [Float]
}
for node in nodes {
    node.x += 1
}
  • 原地修改
  • 没有元素级拷贝
  • cache 更友好

三、真实场景 3:struct + protocol existential

❌ struct + any Protocol

protocol Renderable {
    func render()
}

struct Sprite: Renderable {
    var buffer: [UInt8]
}

let items: [any Renderable] = sprites

性能问题

  • struct 被 装箱(existential container)
  • buffer 不再 inline
  • 访问变成间接

class:

  • 本来就是引用
  • 没有额外装箱

👉 struct 的优势在 existential 下直接消失


四、真实场景 4:struct 在并发环境中被频繁复制

struct Frame {
    var pixels: [UInt8] // 4MB
}

let frame = loadFrame()

DispatchQueue.concurrentPerform(iterations: 4) { _ in
    process(frame)
}

发生了什么?

  • 每个线程拿到一个“值”
  • 第一次写 → 各自复制 4MB
  • 内存带宽被打爆

class:

  • 多线程共享引用
  • 只复制必要的数据

五、真实场景 5:struct 尺寸过大(cache miss)

经验阈值(非硬规则)

struct 超过 64–128 bytes
就要开始怀疑性能了。

原因:

  • 函数参数传递
  • cache line 填充
  • memcpy 成本

class:

  • 永远只传 8 bytes 指针

六、真实场景 6:struct 被当“可变对象”使用

struct State {
    var a: Int
    var b: Int
    var c: Int
}

func update(_ state: inout State) {
    state.a += 1
    state.b += 1
}

在小规模下没问题,但如果:

  • State 里有大字段
  • update 被频繁调用

👉 struct 在被“当 class 用”时,一定慢


七、为什么这些场景 class 更快?

一句话:

struct 的性能优势来自“不可变 + 小 + 本地化”,
一旦违背其中任意一条,优势就会迅速消失。

而 class 的优势是:

  • 稳定
  • 可预测
  • 修改成本固定

八、一个实战判断表(非常有用)

场景更优选择
小对象 / 不可变struct
大对象 / 可变class
并发共享class
热路径频繁修改class
protocol existentialclass
SwiftUI / 值流struct
算法核心struct(小)

九、一句话结论(送你)

struct 是“快的前提条件更多”,
class 是“慢得更稳定”。