上周 @redacted_noah 在 @anchorlang 上发了一篇关于 Zero Copy™️ 的 rant,称其是每个 @solana 开发者的完美 alpha。 我原以为 zero copy 只用于非常大的账户。 我意识到我有时使用它的原因完全不明。 我决定深入研究,让我带你一起了解 👇
@SuperteamFRANCE @SuperteamJapan 首先,我们为什么要讨论零拷贝数据解析? Anchor 的默认数据解析器称为 Borsh。 在调用 Anchor 指令时,Borsh 将把您的账户数据复制到 Borsh 结构中(到一个称为 "堆" 的内存槽,稍后会详细介绍)
当您在指令中包含一个账户时,您使用的格式看起来像这样。
使用零拷贝时,您的代码将如下所示。
那么那里到底发生了什么? 一开始并不明显! 首先,我们需要理解 Solana 如何使用其 Rust 应用数据:堆、栈和 "零拷贝" 空间。
1. 栈: 这是我们存储本地和简单数据类型的地方。 每个栈有 4KiB 的空间,每个函数调用都有自己的 4KiB 分配。 当你超过最大栈大小时,会出现类似于 "在地址 XXXXX 的栈帧 X 中发生访问违规,大小为 X。" 的错误。 在 Anchor 中,解决此错误的第一个方法是使用 `Box` 将数据从栈移动到 "堆" 👇
2. Solana中的堆: 程序默认在BPF虚拟机中运行,堆大小为32KB。这是一次性分配,用于一次完整的调用。 在这里,您将存储更多动态数据类型(Vec,String) 使用Borsh反序列化账户时,数据会复制到堆中,快速占用空间。
3. 零拷贝: 如果你必须绕过堆和栈,因为你的数据预算超出了(大量的CPI,带有大账户),你将使用零拷贝。 零拷贝使你能够在不先分配或复制数据的情况下直接处理数据,跳过反序列化。
何时使用零拷贝是有意义的? 1. 大数据 2. 处理大量CPI 让我们继续:
1. 大数据 假设你想跟踪一份钱包列表,以便能够进行完全的链上检查(如果你需要这样做,可以研究梅克尔树😅,这不是正确的方法) 就像在抽奖中,在进行最终抽奖并选择获胜者之前,存储每个参与者的地址。 这将很容易超过32kb的内存。一个参与者 = 32字节,因此如果你计划成功并为1000个参与者分配空间,这已经占用了整个堆大小(32,000字节) 在这种情况下,你可以使用零拷贝来绕过限制,并能够处理更大的账户,而不会触及堆或栈的限制。
如何修复? 简单!只需在任何地方使用 Zero Copy。 就像这样简单 👇(将 #[account("zero_copy")] 宏属性添加到 RootEscrow 账户) 但是 Zero Copy 也带来了另一个挑战,这正是 Anchor 最初选择 Borsh 的原因:字节对齐。
字节对齐是一种每个 Solana 开发者都应该理解的低级技术,无论是否使用零拷贝。 它要求通过 bytemuck 确保结构体是零拷贝安全的(字节对齐如果处理不当可能会导致恐慌)。 我会很快发布另一个关于这个*令人兴奋*主题的帖子 🔥
与此同时,查看一下 @legendsdotfun,这是我自九月中旬以来一直在开发的产品,并参与了 @colosseum Cypherpunk 黑客马拉松 注册你的产品,给有前景的新团队投票 让每个 Solana 传奇都闪耀 🤝
非常感谢这些大神们: - @redacted_noah - @blockiosaurus - @0xIchigo 感谢你们校对这个实时发现的主题!
5.71K