Design: 标记的 GC 指针

创建于 2016-12-17  ·  41评论  ·  资料来源: WebAssembly/design

我注意到在GC 文档中没有提到标记指针。 我想为它辩护。 当使用 GC 互操作编译可能严重依赖标记联合(例如 Rust 或 OCaml)的语言时,您必须将标记存储为一个单独的字段,这可能非常低效。

即使作为不透明引用类型的初始提议的一部分,公开一种允许标记与引用相关联的方法也是很好的。

我意识到并非所有架构或 VM 实现都能够利用这一点,在某些情况下甚至可能需要装箱。 JS VM 也可能已经使用标签的可用空间来存储其类型信息。 但是,对于可以利用的环境,拥有适当的可供性将非常有帮助。

我可以想象这也与一个可能的 JavaScript 枚举提案有关(我有兴趣在 TC39 中进行探索)。 这可能是该值的 JavaScript 表示。

managed objects

最有用的评论

@shriram ,请放心,我们正在考虑 C++ 之外的问题,并且不会将这些功能视为奇怪或不重要的东西。 支持他们是后 MVP 的议程。 我们可能不一定要添加通用调用/抄送,但更多限制形式的定界延续是不太可能的。 一个可能的方向可能是代数效应处理程序——我们已经提出了结构化异常处理的建议,并且添加恢复可能是提供结构化“堆栈切换”形式并启用您提到的所有控制抽象的“最简单”方法.

所有41条评论

您可以在线性内存中执行此操作,只需在使用前屏蔽指针即可。 我多年来一直在努力考虑这个用例,并且可以使用支持,我有一个计划。

@sebmarkbage你有枚举提案的大纲吗?

对于标签:您是希望 VM 为您提供一个属性“N 位可用标签”以及您自己处理的任何其他内容,还是在您超出未指定的限制时自动神奇地使用辅助存储?

@sebmarkbage ,我们正在考虑这种可能性(尽管您可能主要不是指标记指针,而是指对象标头中的标记,以及用于纯枚举的标记整数)。 但是,由于 VM 和体系结构的多样性,这将相当棘手,并且本质上需要在 Wasm 类型系统中使 sums 和 products 变得原始。 在这一点上,这是否能奏效还很难说。

@jfbastien我还不确定控制或内置它是否更好。现在我也认为在某些情况下使用对象标头标签可能比使用标记指针更好。 一般来说,我喜欢作为作者进行控制,但在这种情况下,您可能需要为每个分支发送大量额外的代码,所以我想我倾向于自动使用辅助存储。

我似乎无法在网上找到枚举提案。 它是值类型的一部分,但我认为我们可以将它与值类型分离。 抄送@littledan

@rossberg-chromium 我的意思是标记指针,但现在你提到了它,我可以看到对象头中的标记在 V8 等一些设计中可能更有用。 我可以看到根据平台选择一个或另一个很有用。 确实很棘手。

@rossberg-chromium 将来在“我们正在考虑”中使用“我们”一词时,能否更具体一些。 据我所知,这个社区小组没有讨论过关于这个问题的想法,我不希望这个社区小组的工作被歪曲。 例如,您可能希望将来将其称为“The v8 团队”,或者为提供成员资格的子组创建一个名称。 如果主席允许私下讨论继续推进该小组的工作方向,而不让成员了解和控制,那么他们能否请回他们的主席。 如果有些主席不是由成员选举出来的,那么他们能否请回他们的主席。

我建议以一种与值类型或类型对象分离的方式来追求标记指针。 例如,您可能希望在此处限制为 2-4 个标记位,而添加到 JavaScript 的 ADT 不应出现此类限制。 抄送@tschneidereit

当@rossberg-chromium 谈论“我们”时,他指的是在从事 WASM 工作的人们之间的非正式场合。 考虑到我坐在离他 3 米远的地方,建立特定的小组来祝福这次交流似乎需要很多开销:-)

我认为标记不如和类型通用。 不幸的是,标记也与底层机器的字宽(以位为单位)相关联。 然而,不受限制的和类型会将它们的值表示完全留给引擎,一些语言实现者可能对此感到不舒服。 因此,限制总和(例如,仅在原始类型和引用的某些子集上)可能会导致高效的值表示,而不会在引擎中带来很多复杂性。

@titzer巧合的是,“我们”(@lars-t-hansen,我和其他一些 wasm 人)正在讨论某种限制总和的相同基本思想,这种总和总是允许使用标记词表示。

今年夏天的一次谈话中得出的想法是,在一个有单独堆 gc-able 对象(可能是 TypedObjects)的设计中,可能会有两种新的 wasm 类型:

“指针”是对 gc-able 堆中某些 gc'd 事物的不透明引用(可能为 null)。 在 Wasm-32 上是 4 个字节,在 Wasm-64 上是 8 个字节。

“盒子”是指针大小的三元组:

  • 指针指示符位(1 位)
  • 标记位(2 或 3 位,可能取决于字长)
  • 值(如果设置了指针指示符,则为指针,否则为整数)

一些合理的操作:
boxptr(pointer, tag) -> Box[1, tag, pointer]
boxval(integer, tag) -> Box[0, tag, integer & ~15]
ptrbitof(Box) -> 0 或 1
tagof(Box) -> 标签
ptrof(Box) -> Pointer 或 null 如果框不是指针框
valof(Box) -> 整数值或 0 如果框不是整数框

除了当前类型之外,wasm 中的局部和全局变量将是框类型或 ptr 类型。 指针和框不能存储在线性堆中。

至少,我所描绘的应该适用于许多动态语言并且具有高性能; 在实践中,3 个标记位(指针位加两个位)通常是可以的,之后可以在对象中存储额外的标记位。 指针指示符和标记位存储在字的低位。

允许将整数框重新解释为整数值也可能很好,如果 wasm 实现可以证明该框是整数框,则这是一个空操作,同上,允许将整数转换为整数wasm 实现的框只是屏蔽了指针位。 这可能需要确定哪个位是指针位。 无论如何,这为整数提供了 wordsize-1 位,“标签”只是较大值中的一个位字段。

@lars-t-hansen 我认为需要类型测试运算符,以便代码可以提升if(is_int(p)) ...然后编译器会知道p被标记为整数而不是指针并且可以优化valof()编译器不必进行提升。 我认为不需要ptrof()因为运行时无论如何都需要检查使用类型,而整数只是另一种要测试的类型。

不确定是否将标记称为“装箱”,因为“装箱”通常意味着在堆框中分配。

还有机会测试或屏蔽高地址位,同时屏蔽低标记位,证明索引在线性堆内。 两年来,我一直在努力使这一点得到认可,它需要更多地关注类型派生的改进,目前在 Mozilla 代码中是 P5,请您考虑一下。

@wllang ,是的,我没有考虑类型测试操作码,因为我还没有在脑海中解决这个问题,特别是,是否会有与此类测试相关的任何性能保证。 is_int(x) 等价于 eqz(ptrbitof(x)) ,对于初学者来说,这不是一个非常难以识别的模式; is_ptr(x) 只是 ptrbitof(x)。 由于它是一个堆栈机器,因此在假设的单字节操作“i32.isint”和由两个一元单字节操作“i32.ptrbit; i32.eqz”组成的两字节操作之间确实存在细微差别。

但确切的形式并不重要,重要的是效果。 似乎很清楚,在“if(is_ptr(x)) { ... } else { ... }”中,编译器可能会假设 x 是 then 块中的一个指针,而 else 块中的一个整数,如果它可以证明值这些块中的 x 与测试中的 x 的等价性。 这对于基于 SSA 的系统来说似乎并不难。 但是您是否需要在这些块中省略标签检查?

假设我们允许一个字节码产生两个值; 滥用语法,操作 ptr_i32.ptrof 将返回一个指针(或 null)和一个布尔值。 那么编译器必须“知道”什么就不会有任何问题了,它只会有一个值,它是构造的指针。 (还有一个关于这些类型化对象的类型系统的问题,以及在何种意义上键入指针,但我认为这可能与标记正交。)

(稍微离题,关于是否应该在具有某些简单标签的可能为 int 的框上允许算术和按位运算,以及某种状态代码返回,类似于 SPARC 上的标签添加指令,还有其他有趣的想法。i32x2.add k,如果 a 和 b 都是具有较低 k 标记位 == 0 的 int 框并且没有溢出,则 a, b 将导致 (a+b, 0),否则为 (garbage, 1)。i32x2.or k, a, b同上 a|b。这些操作在 wasm 级别很难有效表达,但在硬件级别很容易实现。但这些操作可能过于专业,不值得——必须证明目标很有趣Lispish 语言到 wasm。)

至于高地址位......我不知道你以前写过什么。 您想实现某种类型的 BiBoP 方案还是想使用高位作为标签?

(参考您提到的 mozilla 错误?)

必须证明将 Lispish 语言定位到 wasm 很有趣

如果您认为 WebAssembly 不应该尝试以有效的方式支持所有编程语言,那么我认为您低估了 Wasm 的重要性。 如果做得好,Wasm 将接管世界。 Wasm 将降低操作系统、CPU 架构和(我希望)编程语言的锁定能力。 每个人都将使用它,用于离线和在线应用程序,以及客户端和服务器。

那个世界不应该是某些编程技术和编程语言注定效率低下和不切实际的世界,因为 WebAssembly 设计者认为这些语言不够“重要”而无法支持。 我们现在认为理所当然的很多东西,以及新语言的特性,都是从晦涩的学术语言和/或 Lisp 开始的。

我们的目标应该是支持所有语言和所有具有良好性能的编程技术,例如,始终在给定技术最佳实现的 2 倍内存和 CPU 时间内。 首先,一个高性能的 Wasm 实现应该能够有效地托管另一个高性能的 Wasm 实现或在其中运行的 JVM 或 CLR(所有这些都将利用外部 Wasm 引擎进行代码生成),或者 Lisp VM 或无堆栈 Python。 除了所有这些暗示之外,我还想看到延续(或至少某种微线程系统)、函数内快速 goto 的“一流”标签……嗯,基本上是未来功能中的所有内容,然后是一些。 虽然必须优先考虑工作,但我们不要忘记所有这些都很重要。

@lars-t-hansen 我很想更好地了解您对 GC/不透明堆的设计理念(以及其他所有人的想法)。标签位对 WebAssembly VM 有何影响?

我们如何制作“通用”虚拟机? 一些东西

  • 支持精确、压缩、分代 GC
  • 支持数组切片和指向对象或数组中间的指针,而不仅仅是开头
  • 支持基于“瘦”和“胖”指针的对象系统——或者两者都有! (瘦指针依赖于存储在指向的对象中的类型信息,而胖指针将该信息存储在指针旁边的一两个额外单词中)
  • 允许可以在指针和整数之间动态切换的“可能指针”
  • 有效地支持联合类型(指向 A 或 B 或 C 的指针 - 它必须是其中之一,但哪个是动态的)
  • 在我们的安全限制下,尽可能允许客户端控制内存分配和 GC。
  • 支持不透明引用——尽管这似乎与将 Wasm 客户端代码置于“驾驶座”的总体目标(例如让客户端执行自己的 GC 算法)相矛盾
  • 支持我什至没想过的其他东西

免责声明,此评论并非针对 OP 中的问题。

如果您认为 WebAssembly 不应该尝试以有效的方式支持所有编程语言,那么我认为您低估了 Wasm 的重要性。 如果做得好,Wasm 将接管世界......

我不认为“接管世界”是 WebAssembly(字面和比喻上的 😉)的一个伟大目标。 由于大多数浏览器都是常青树,因此标准在世界上的发展速度更快,而 WebAssembly 旨在填补这些标准中的空白,为处理器敏感的应用程序提供快速可序列化和传输优化的指令集。 考虑到新发现的延展性使我们能够更轻松地处理新的问题域,通过牺牲一些细节来尽可能地满足这一要求,这将使处理需要运行时和垃圾收集的语言变得更容易,这对我来说似乎是合理的做法。随着集体见解和经验的积累,快速改进我们对现有解决方案的解决方案。

我个人并不认为 WebAssembly 没有足够的资金来实现这些庞大的应用程序; 我个人从非面向浏览器的基于 Internet 的虚拟应用程序的概念的角度对 WASM 产生了兴趣,这些应用程序可以像 html 和 css 一样安全地共享和享受,而无需附加到 Web 浏览的文档发布现实。 特别是,我认为 WebAssembly 的核心义务并没有完全将其排除在讨论中,该讨论推进了满足所有这些可能性的 ISA 的可能性,无论该 ISA 是否是您给定问题域的 WebAssembly。

我不认为“接管世界”是一个很好的射门目标

只是我的预测。 我并不是说接管世界是目标——我是说 WebAssembly 具有独特的定位,因此它很有可能在没有任何人专门试图实现这一目标的情况下接管世界。

那么,标签位对 WebAssembly VM 的影响是什么? 它们是为了什么?

我从上面提取了我的评论并进行了详细说明,以便我们可以有一个地方来观察想法的演变,而不必每次都浏览整个线程:
https://github.com/lars-t-hansen/moz-sandbox/blob/master/wasm-tagging.md

这篇文章也有更多关于“为什么要标记?”的理由。 以及对安全性、现有技术等的一些评论。

@qwertie ,我不想阻碍 Wasm 或阻止它支持任何特定语言。 但是我们可能应该承认,在 WebAssembly 的上下文中,某些源语言比其他源语言更重要,而且要很好地支持某些语言需要付出很大的努力。 对于 Scheme,我们需要闭包、尾调用、一流的延续、动态类型的对象字段、动态类型的变量和高效的泛型算术。 其中一些可以以可接受的成本从更简单的结构中实现。 可以通过将所有帧推送到 gc'd 堆和/或手动管理的堆栈上来模拟尾调用和延续,但您可能不希望那样做,这可能太昂贵了。 所以你需要原生支持。 但是,如果 Scheme 是 call/cc 的唯一用例,我认为要获得接受将是一场艰苦的战斗; 毕竟,Scheme 是一种死语言。

标记讨论有点吸引人,因为到目前为止,我们这边的讨论一直是关于采取一些我们必须拥有的东西(指向不透明堆的指针)并添加一个便宜的功能,许多人认为该功能有用且性能良好且可移植(低位标记),然后打开另一个想法(将非指针数据存储在标记事物的指针部分)。 这对支持 Lisp 和 Prolog 以及其他一些语言大有帮助。

@sebmarkbage确实问了一个更通用的标记方案,设计空间相当大,但 IMO 我们应该由用例驱动,用例可能是已经证明其价值的特定实现技术,对于足够广泛的语言习惯于“重要”。 (对于仍然不受该策略支持的语言,如果您希望这些语言在 Web 上运行,则总是有平坦的内存和滚动您自己的 GC。)

标记讨论有点吸引人,因为到目前为止,我们这边的讨论一直是关于获取我们必须拥有的东西(指向不透明堆的指针)并添加一个廉价的功能

我不同意指向不透明堆的指针是 WebAssembly 必须具有的功能。 对于 WebAssembly 对动态和 GCed 语言的支持,我可以想到三个可能的方向:

  1. 只担心成为非 GCed 静态语言的好 VM。
  2. 公开 JS 可互操作的类型化和垃圾收集堆。
  3. 公开在 WebAssembly 中实现动态类型语言(或静态类型 GC 语言)所需的原语:例如 JIT 代码补丁、用于垃圾收集线性内存的堆栈遍历等。

MVP 将其范围限制在第一个方向,而没有在 MVP 之后承诺。

这个问题和GC 设计页面假设第二个方向。 在该假设的上下文中,为 GC 指针公开标记位作为对“必须”支持的不透明 GC 指针的廉价扩展确实有意义。

但是,我相信第三个方向是 WebAssembly 最好的长期方向。 第二个方向似乎是通过使 WebAssembly 的浏览器 DOM 更容易工作来推动的。 然而,没有人会直接在 WebAssembly 中编写代码,因此需要一些语言支持,这可以使用过程 JS API 来访问 DOM,就像对 DOM 进行低级访问一样有效。

第二个方向还允许 JVM/.net、Scheme 或 Ruby 的 WASM 后端重用 JavaScript 垃圾收集器。 然而,垃圾收集器并不是万能的:可能的结果似乎是引入了对 WASM VM 的永久要求,以包含类型化 + GC 的堆,这不能满足许多​​应用程序的性能要求。

第三个方向需要更多的工作,但从长远来看,应该允许更简单的独立 WASM VM 并降低浏览器 VM 的复杂性,因为当今更多的英雄优化工作从 JavaScript VM 迁移到 WASM 沙箱。

我之前创建了 #733 来讨论将 JavaScript GC 堆暴露给 WebAssembly 与暴露用于实现垃圾收集器的原语的相对优先级。 也许关于 WASM GC 方向的进一步讨论可以在那里继续,并且这个问题可以继续集中在标记的 GC 指针上,并假设将向 WASM 添加对指向 GC 堆的不透明指针的支持。

@lars-t-hansen 你的文章没有提到它与垃圾收集的关系,或者不透明引用的内存区域的想法。 是否有一个潜在的假设,即不透明的引用“堆”或“表”将是一个简单的“框”数组(或呃......“标记的Ptrs”...... TPs?)这样任何一个框都可以引用一个 JS 对象,因此它会被 JS GC 保持活动状态?

在查看文档后,我突然想到,也许您的想法不是让 WebAssembly 在 Wasm 端进行垃圾收集 - 而只是允许不透明的引用区域保存对 JS 对象的两个引用(以防止它们被收集)和其他东西,比如整数和对自身的内部引用。 然后可以为恰好支持不透明参考区域的 WebAssembly 构建一个(不受信任的)GC。

我变热了吗?

关于一流的延续 - 这是我不确定我们需要的一个功能。 协程或轻量级堆栈,当然,但延续? 除了构建某种作为堆栈集合的光纤/协程/线程之外,是否有人真的将它们用于其他任何用途? 也许用 switch-stack 操作码支持某种“轻量级”堆栈就足够了。 在这种情况下, call/cc在 WebAssembly 下将不可用,但人们倾向于用call/cc构建的东西将可用。

我同意@AndrewScheidecker ,因为专注于公开原语将提供灵活性,允许当前和未来的所有编程语言以 WebAssembly 为目标并有效地运行代码(并为不同的工作负载使用不同的垃圾收集器)。 另一方面:

  • 我非常关心互操作性; 拥有一个内置的垃圾收集器可以使不同的垃圾收集语言更容易进行互操作。
  • 垃圾收集器可能是大型软件,必须从每个网站下载不同的 GC 可能会很麻烦

所以我想我希望有某种默认 GC,但是在 Wasm 中实现自定义 GC 不应该比使用默认 GC 有很大的缺点。

@AndrewScheidecker ,公平的观点 - 我们必须将不透明的指针指向一个理所当然的。 我建议我们按照您的建议在第 733 期继续讨论。 不过,顺便说一句,我觉得你的说法“从长远来看,第三个方向 [...] 应该允许更简单的独立 WASM VM 并降低浏览器 VM 的复杂性,因为当今更多的英雄优化工作从 JavaScript VM 迁移进入 WASM 沙箱。” 需要参数和代码来支持; 显然这是真的。

@qwertie ,我在这里的一般假设是会有 wasm locals 和 Ptr 和 Box 类型的全局变量; 不透明对象可以具有静态类型为 Ptr 或 Box 的字段; 并且系统的 GC 将跟踪这些字段,具有关于其内容的完美可靠的信息。 这个想法不是 - 在这里 - 支持某种类型的可插拔 GC,我认为这是一种不同类型的项目。 (同样,这个讨论开始于“我们可以向不透明指针添加标签吗?”。)

2017 年 1 月 4 日,星期三,下午 2:59,Andrew Scheidecker < [email protected]

写道:

标签讨论有点吸引人,因为到目前为止,
我们这边的讨论一直是关于采取我们必须采取的措施
有(指向不透明堆的指针)并添加一个廉价的功能

我不同意指向不透明堆的指针是 WebAssembly 的特性
一定有。 我可以想到 WebAssembly 的三个可能的方向
支持动态和 GCed 语言:

  1. 只担心成为非 GCed 静态语言的好 VM。
  2. 公开 JS 可互操作的类型化和垃圾收集堆。
  3. 公开实现动态类型所需的原语
    WebAssembly 中的语言(或静态类型的 GCed 语言):例如 JIT
    代码修补,用于垃圾收集线性内存的堆栈遍历等。

MVP 已将其范围限制在第一个方向,而没有承诺
它在MVP之后。

是的,这是非常有意地阻止功能蔓延并确保
成功。

在各种上下文中,我们讨论的是作为 _extension_ 的托管数据
到 WebAssembly(我什至考虑名称托管数据扩展,或 MDX,
成为一个好的候选人)。 托管数据扩展将允许 WASM 代码
分配与线性内存分开的低级结构
由 WASM 引擎管理(即 GC)。 将输入结构以允许
未装箱的字段,并且足够低级以允许实现
Java 等高级语言的对象模型(带有 vtables、class
指针等)、Go、函数式语言等。

将托管数据类型、值和运算符定位为扩展
将有可能拥有一个简单的核心 WASM 实现
不支持扩展。 在这样的实现上,有可能
(虽然需要做一些工作)来实现使用
线性存储器的一部分(或单独的线性存储器)来模拟这些
结构并实现 GC,但在性能上处于劣势。

这个问题和 GC 设计页面http://GC.md假设第二个
方向。 在该假设的上下文中,为 GC 公开标记位
指针作为对不透明 GC 指针的廉价扩展确实有意义
“必须”支持。

但是,我相信第三个方向是最好的长期方向
WebAssembly。 第二个方向似乎是为了更容易
使用来自 WebAssembly 的浏览器 DOM。 然而,没有人会写
直接在 WebAssembly 中编写代码,因此需要一些语言支持,
并且可以使用过程 JS API 来访问 DOM,就像
就像对 DOM 进行低级访问一样有效。

第二个方向也将允许 JVM/.net 或 Scheme 的 WASM 后端,
或者 Ruby 来重用 JavaScript 垃圾收集器。 然而,垃圾
收藏家并非一刀切:可能的结果似乎是
引入对 WASM VM 的永久性要求,以包含类型化 + GCed
堆不能满足许多​​应用程序的性能要求。

第三个方向需要更多的工作,但从长远来看,这两个方向都应该
允许更简单的独立 WASM VM 并降低浏览器 VM 的复杂性
随着当今越来越多的英勇优化工作从 JavaScript VM 迁移
进入 WASM 沙箱。

我之前创建过 #733
https://github.com/WebAssembly/design/issues/733讨论
将 JavaScript GC 堆暴露给 WebAssembly 的相对优先级与
公开用于实现垃圾收集器的原语。 或许更进一步
关于WASM GC方向的讨论可以在那里继续,这个问题
可以继续关注标记的 GC 指针,并假设支持
将被添加到 WASM 以获取指向 GC 堆的不透明指针。


你收到这个是因为你被提到了。
直接回复本邮件,在GitHub上查看
https://github.com/WebAssembly/design/issues/919#issuecomment-270484689
或静音线程
https://github.com/notifications/unsubscribe-auth/ALnq1EpdSRskLiuHx4v5M83T4IS9wmx1ks5rPAgggaJpZM4LPxE5
.

@lars-t-hansen,很明显整数需要显式标记/取消标记,但是在类型系统中区分装箱和未装箱引用类型的用例是什么? 是否有任何无法直接在装箱引用上提供的未装箱引用操作? 在这种情况下,您不需要介绍前者。

@titzer Re:“模拟这些结构并实现 GC,尽管存在性能劣势” - 如果使用线性内存的 wasm 核心无法在没有性能劣势的情况下实现包括 GC 的对象系统,那么这似乎是一个核心设计问题。

这些“性能劣势”的本质是什么?

我对这些相关项目的经验的解释是,网络浏览器供应商是问题的一部分,或者说达成一致是一个令人难以置信的挑战,因此社区需要的是网络浏览器供应商提供高性能低级别语言,以便开发可以免于需要在 Web 浏览器供应商之间达成协议。 因此,我呼吁为了 Web 社区的利益继续专注于该目标,并且托管对象会分散该目标的注意力。

我只会从 wasm 中删除更高级别的托管对象和托管 GC,将其从未来的功能中删除,专注于核心并要求在线性内存中的对象系统和 GC 实现存在性能问题的组报告它们,以便他们可以在核心设计挑战中考虑。

我们已经知道线性内存中标记点的一些挑战以及解决这些问题的路径——我尝试过的改进的整数类型推导,但到目前为止未能达成一致; 以及 SSA 编码的潜在优势,使这种推导更加清晰; 以及屏蔽高位同时屏蔽低位的好处。

我只想从 wasm 中删除更高级别的托管对象和托管 GC,将其从未来的功能中删除

我非常不同意这种观点。 托管对象并不是要简化托管语言的编译器编写者的生活。 托管内存在未来为 wasm 带来的主要、有形的特性将是允许与 JavaScript 世界的其他部分

没有办法可以实现从WASM内存(潜在循环)引用JavaScript中的线性内存模型对象。 如果没有最终解决这个核心需求,那么 ClojureScript、Scala.js 或任何其他具有与 JavaScript 良好互操作性的编译为 JS 的语言就没有希望编译为 wasm。

将语言内功能编译为任何内容都很容易。 我们真的不需要目标支持。 然而,如果没有目标的支持,编译语言特性是不可能的。 IMO wasm 最终应该为我们提供与 JavaScript 对象模型互操作的所有工具(并通过这个通用模型,在 compile-to-wasm 语言之间)。 这需要一个内置在 wasm 规范中的托管内存模型(可能作为扩展,如果我们想以这种方式对其进行标记)。

@sjrd最好详细说明这些“挑战”,以便解决它们。 所以你似乎提出的节目阻止者是:

  • 将 wasm 沙箱中的整数映射到 JS 对象,但这似乎很可能使用哈希表或数组,它只是保持效率有多高,这需要多有效? 这已经在游戏等中完成了。满足指针安全性的更有效的散列可能是可能的,所以如果有必要,让我们探索一下。

  • 你还提出了循环引用的问题,所以我想你关心的是 JS GC 如何清除这些在 wasm VM 中存储为整数的 GC 指针? 但是从 JS 到线性内存中的对象的指针呢?

这些问题一般不能通过向 wasm 添加托管对象和托管 GC 来解决,除非承认一般来说 wasm 代码使用托管对象和托管 GC 并且这将基本上放弃核心 wasm 项目将其变形为更高级别的 VM 设计。

所以我们可以探索一些解决方案:一个调用 wasm 沙箱的 JS GC 钩子来清除可能引用回 JS 对象的指针,并且可以通过某种定义的方式将这些返回给 JS GC。 这可能只是一些已定义的 FFI 调用,不会给 wasm 核心设计和沙箱增加负担。 这是需要发生的思维类型,不要试图依靠 wasm 来解决所有问题。

@sjrd您还可以查找“分布式垃圾收集”,这可能是一个框架,可以解决您的用例,而不会成为 wasm 的负担。 我不确定 JS 是否拥有所有需要的支持,但如果需要,可能会有更多人对 JS 的支持添加这种支持,用于云对象存储等,这可以在 wasm CG 之外完成。 wasm 方面只是整数 wasm 代码,因此可以是一个单独的项目(如果 Web 浏览器供应商和/或 Web 社区不同意,则可以是多个项目)。 也许对于许多应用程序来说,简单的策略(例如引用计数、弱指针和租用指针)就足够了,而无需跨 wasm 和 JS 实例进行全局垃圾收集的复杂性。 另一种选择可能是查看是否可以将这些框架中的更多 JS 代码移动到沙箱中 - 有人会为 wasm 编写一个 JS 解释器,该解释器对作为较大代码库的一小部分的其他 JS 代码具有足够的性能。

@罗斯伯格铬,

很明显,整数需要显式标记/取消标记,但是在类型系统中区分装箱和未装箱引用类型的用例是什么? 是否有任何无法直接在装箱引用上提供的未装箱引用操作? 在这种情况下,您不需要介绍前者。

您不知道 Box 是否包含引用。 既然如此,直接在 Box 上的指针操作(这本身没有问题)必须检查指针位并剥离任何标签。 Ptr 上的指针操作永远不需要这样做。 所以从我的角度来看,这只是一个效率问题。

@lars-t-hansen,啊,所以 IIUC 原始指针将被输入但盒装指针将被输入? AFAICS,这不是您想要的因式分解,因为每次通过装箱指针进行的访问都首先需要进行运行时检查——装箱指针是您可以存储在 GCed 内存中的唯一内容。 因此,遍历内存图需要在每条边上进行运行时检查。

我想到了一些稍微不同的东西:不透明引用类型的家族ref(T) ,其中 T 是结构/数组、函数(即函数指针)或整数(即标记整数)。 此外,您有一个类型anyref ,它是所有引用的联合/超类型,您可以在其上执行类型测试和检查向下转换。 这样,您只需要在实际处理联合时进行运行时测试。 此外, null 将是anyref类型中的值,但不是ref(T) ,因此空检查同样可以被隔离并且在代码中是显式的。

@rossberg-chromium,确实我已经掩盖了我认为我们将如何在此处跟踪类型信息。 我想象在某个时候 Boxes 可以跟踪类型信息,即 Box(Ptr) 其中 T 是结构类型将产生一个已知的数据,该数据包含一个标记的指向 T 的指针或一个(标记的)整数。 在这种情况下,直接取消引用框仍然需要检查指针性(它可能是一个 int),并且仍然需要删除标签(因为标签设置了未知位)。

你的 ref(T) 想法显然是相关的,但不允许标记指针,如果我理解正确的话,至少不是用户定义的标签,这是我试图用我的设计解决的问题。

(+1 通过类型系统以某种方式限制 nullptr,我认为这与标签正交。)

@lars-t-hansen,我明白了。 嗯,我认为打字实际上是标记功能的关键。 现在我再考虑一下,我真的不明白如何在不将标记类型或和类型显式引入 Wasm 类型系统的情况下实现它(缺少某种形式的依赖类型,我认为我们想要避免)。

但是如果我们无论如何都需要某种形式的标记类型,那么在这个更高的抽象层次上对标记进行完全建模似乎更有吸引力,而不是干预最低层次的指针值。 这为实现提供了必要的余地,以他们认为合适的最佳方式存储标签,并避免对架构和 VM 的依赖,或将标签限制为公分母。 例如,对于 V8,为指针支持 1 个以上的附加标记位本质上需要重新实现 V8 的一半,这是我们不太可能注册的——特别是因为这意味着在 32 位平台上需要大量内存成本。 另一方面,V8 有很多方法可以在堆值的映射(“隐藏类”)中有效地存储更大的标签,而不会浪费每个值的空间。

@wllang

最好详细说明这些“挑战”,以便解决它们。

我同意。 我已经计划这样做一段时间了,但还没有开始做。

将 wasm 沙箱中的整数映射到 JS 对象,但这似乎很可能使用哈希表或数组,它只是保持效率有多高,这需要多有效?

IMO 需要至少与在 JavaScript 中编写操作堆对象的相同代码一样有效。 我认为我们无法通过整数映射达到这种性能水平。

从您在这里和其他线程中的几篇帖子来看,您似乎非常关心 wasm 核心在非托管语言方面的性能。 然而,对于托管语言及其优先级,与与 wasm 的更深入集成相比,您可以通过解决方案解决核心问题,这些解决方案本质上是次优的、性能方面的。 为什么我们像这样区分托管语言?

这些问题一般不能通过向 wasm 添加托管对象和托管 GC 来解决,除非承认一般来说 wasm 代码使用托管对象和托管 GC 并且这将基本上放弃核心 wasm 项目将其变形为更高级别的 VM 设计。

它可以被寻址,例如,如果 wasm 有两个内存空间:它现在拥有的线性内存,主要由编译为 wasm 的非托管语言使用; 以及支持联合 GC 与 JavaScript 堆的 GC 堆,主要用于编译为 wasm 的托管语言。 请注意,托管语言可能还会将线性内存用于“堆外”分配(例如,Java 中的ByteBuffer.allocatedDirect() ); 和非托管语言也可能将托管内存用于与 JavaScript 的互操作性场景。

这可能只是一些已定义的 FFI 调用,不会给 wasm 核心设计和沙箱增加负担。

我不是 GC 专家,但我有一种感觉,“只有几个公开指定的 GC 挂钩”很可能是 wasm 核心设计和/或 VM 实现的重大负担。

另一种选择可能是查看是否可以将这些框架中的更多 JS 代码移动到沙箱中 - 有人会为 wasm 编写一个 JS 解释器,该解释器对作为较大代码库的一小部分的其他 JS 代码具有足够的性能。

这将给所有相关人员带来明显更高的总体负担。 还有 TBH,在大多数情况下,wasm 必须链接到实际的 JS VM 时,在 wasm 中实现 JS VM 感觉真的很荒谬。 此外,它并没有解决与浏览器 API 通信的问题,浏览器 API 本质上是 JavaScript API。

@sjrd

IMO 需要至少与在 JavaScript 中编写操作堆对象的相同代码一样有效。 我认为我们无法通过整数映射达到这种性能水平。

索引到数组很快,也许是一些机器代码指令。 哈希查找与命中类似。 JS JIT 在内部使用大量带有表和测试的缓存,因此性能可能是 JS 的特征。

从您在这里和其他线程中的几篇帖子来看,您似乎非常关心 wasm 核心在非托管语言方面的性能。 然而,对于托管语言及其优先级,与与 wasm 的更深入集成相比,您可以通过解决方案解决核心问题,这些解决方案本质上是次优的、性能方面的。 为什么我们要像这样区分托管语言?

如果您定义“托管语言”,那么这听起来可能不一样:) Wasm 应该能够支持用 wasm 核心代码编写并存储在 wasm 线性内存中的高效对象系统,并且此代码将“管理”对象系统在沙箱中编写的其他代码,几年来我一直在努力为这个用例获得更好的性能。 所以我一般不会对“托管”代码有偏见,我不仅仅关注 C 代码的性能。

您似乎关心的情况是与在同一进程地址空间中运行的 JS 运行时管理的对象集成。 但是假设 wasm 的一个实现想要将 wasm 沙箱放在一个单独的沙箱进程中,那么你已经超出了你似乎关心的用例的范围 - 并且将一个 wasm 实例放在一个单独的进程中可能很有吸引力安全性和性能。

它可以被寻址,例如,如果 wasm 有两个内存空间:它现在拥有的线性内存,主要由编译为 wasm 的非托管语言使用; 以及支持联合 GC 与 JavaScript 堆的 GC 堆,主要用于编译为 wasm 的托管语言。

它不起作用,因为外部指针不能存储在线性内存中,因此线性内存中的对象系统无法存储它们,因此不实用,因此将被迫使用托管外部指针来实现它的对象系统。 当然,代码可以在线性内存中分配一些缓冲区等,但它无法在与外部指针集成的线性内存中管理它自己的对象。

我不是 GC 专家,但我有一种感觉,“只有几个公开指定的 GC 挂钩”很可能是 wasm 核心设计和/或 VM 实现的重大负担。

定义一些导入/导出的函数名并不是 wasm 设计的负担,但 JS GC 实现的负担可能相当大,如果全局 GC 是一个障碍,则需要思考。

这将给所有相关人员带来明显更高的总体负担。 还有 TBH,在大多数情况下,wasm 必须链接到实际的 JS VM 时,在 wasm 中实现 JS VM 感觉真的很荒谬。 此外,它并没有解决与浏览器 API 通信的问题,浏览器 API 本质上是 JavaScript API。

对于 wasm 消费者来说,负担会低得多! 一个低得多的攻击面。 它还可以实现更高的性能和更安全的实现(在单独的过程中)。 这也将是一个低得多的设计负担,这似乎非常重要 - 您期望在 Web 浏览器中部署一个功能(如果它甚至可能成功)与承包人编写实现相比需要多少努力和资源对于 wasm 核心的功能,您会估计十倍的资源还是一百倍的资源,成功的机会是多少,也许是十分之一或一百分之一?

考虑到 wasm 实例可能在一个单独的进程中,wasm 可能能够与可能被缓存的 VM 代码链接,并且 Web 浏览器可能会捆绑一个版本和一个交换它的机制,这也可能不是那么“荒谬”。 想象一下能够用户选择或升级您的 JS VM(用于在 wasm 沙箱中使用),能够将其替换为另一个版本以进行调试等。另外请记住,这也是关于开放生态系统,拆分 JS在某种程度上,所以人们可能会包含一个微型基本解释器或微型 python 或 LUA 或 CIL VM 或 JVM 等。无论如何,一个简单的 JS 解释器有多重,如果它相对较重,那么也许其他框架无论如何都会显示出好处?

我会支持将较低级别的 JavaScript API 提供给 wasm 实例,而无需处理 JS 对象,更像是一个操作系统层,这似乎很有可能。

我还支持 wasm 编码足够灵活,可以表达您希望的 JS VM,这似乎不是一个很大的负担,无论如何都会有助于面向未来的 wasm。 我只是想知道我是否会在网络上看到高性能的低级 VM。

@罗斯伯格铬

另一方面,V8 有很多方法可以在堆值的映射(“隐藏类”)中有效地存储更大的标签,而不会浪费每个值的空间。

Fwiw 建议将指针标签存储在“类”对象中听起来确实有点奇怪,因为这里讨论的指针标签是针对每个指针而不是针对每个类的,也许 v8 确实将指针存储在多个单词中,但这会令人惊讶并且有趣的设计? 也不可能将这些标签存储在指针指向的对象中,因为该对象可能是在标签位中编码的立即值。

@sjrd只是为了确定 GC 问题在本论坛(专利)中提出了解决方案,这是一个解决方案。 JS GC 添加了一个在 JS 指针和整数(wasm 侧指针)之间映射的新表。 它可能会直接在线性内存中维护表的 wasm 端,因此 JS 表引用了一个数组缓冲区和一个范围。 在每一侧都有一个live标志和每个指针的引用计数。 当一个指针被添加到表中时,现场标志被设置,并且引用计数在表的那一侧被设置为一。 当任一方的 GC 运行时,它会将清理这些表推迟到最后,因此在这一点上,它知道相应侧的这些指针是否具有来自表外部的来自该侧的活动指针,并更新相应侧的活动标志。 如果活动标志清除并且另一侧的引用计数为零,则指针将被回收并从表中删除。 处理完所有这些表后,GC 继续清除活动指针,使其引用的任何内容保持活动状态。 在 JS 端,这些指针要么是只写的,要么在 JS 端被标记为不存在时变得不可读——wasm 端 GC 将遵循类似的语义(保存表的线性内存仍然可读,但代码强制执行)对象系统不变,它们不可读)。 在任一方的 GC 之后,如果存在标记为 not live 的指针,则会触发异步调用,这可以调用另一方以更新引用计数,当它变为零时,该指针将在下一次 GC 中回收。 这些表可用于同一线程外的对象存储的全局 GC,并且 JS 社区可能对此类功能更感兴趣。

@wllang ,地图/隐藏类是跟踪单个值表示的常用实现技术; 它们只是与语言级别的类概念松散相关。

@rossberg-chromium 根据我对 v8 中隐藏类的理解,它们存储为对象中的一个插槽。 这个 PR 是关于标记的指针,所以即使将标记存储在单独的对象槽中似乎也不相关,很少将它们存储在隐藏类中,而是需要将它们存储在指针中,因此实现只能自由决定哪些位要使用的指针。 或者你有什么其他的想法,我只是不明白。 也许这些幻灯片对其他人有帮助http://thlorenz.com/talks/demystifying-v8/talk.pdf

@wllang ,我说的是公开 _where_ 标签实际存储可能没有意义,因为体系结构和 VM 具有严格且截然不同的约束。 对 Wasm 的标记支持需要足够抽象以允许多种实现策略。

包含在 GC 提案 (https://github.com/WebAssembly/gc/) 中作为可能的扩展。

嘿@lars-t-hansen 和@qwertie — 我将成为这里的异常者。 延续是一个不受尊重的特征; 然后,人们最终不得不艰难地重新发明它们。 一旦你有了它们,你就会自动拥有协程、微线程、生成器、goroutines,你有什么,免费的。 _而且_你有一个原始物,可以用它来构建更多你之前没有想到的东西; 例如:

  • Pyret 的reactor功能和 DrRacket 的big-bang功能为您提供了编程语言中一流的事件循环抽象。 您需要某种类似延续的支持才能在浏览器中实现它。

  • DrRacket 的send/suspend功能(因为被 Smalltalk Seaside 框架和其他一些框架模仿)——用于直接风格的服务器端 Web 编程——需要几乎完全的延续,如果你有的话,实现起来很简单。

请注意,这两个都不是call/cc本身——它是一个不相关的、漂亮的程序员抽象,碰巧用延续很容易构建,没有则不可能构建。

WeScheme [http://www.wescheme.org/] 和 Pyret [https://www.pyret.org/] 的实现者都为缺乏它付出了巨大的代价。 两者都痛苦地、缓慢地通过程序转换来获得它,以启用这些类型的功能,并伴随着巨大的性能损失和复杂性开销。

如果您正在构建自己的_面向用户的编程语言_,那么说“我下令不得进行非本地控制转移”是完全合理的。 如果你正在构建一个 _assembly_,我觉得你必须超越“延续很奇怪,无论如何只有 Scheme 有它们,谁使用 Scheme 来解决这个问题”,并考虑现代语言拥有的大量执行控制原语,人们不断发明.

由于浏览器人员决定既不支持延续也不支持协程/堆栈切换,这是一个有争议的问题,但我很好奇 - 你能描述这些特性的作用,以便我明白为什么不能使用协程构建这些特性/stack-switching 作为原语?

大多数情况下,协程就足够了。 他们肯定会成功
这里。

@shriram ,请放心,我们正在考虑 C++ 之外的问题,并且不会将这些功能视为奇怪或不重要的东西。 支持他们是后 MVP 的议程。 我们可能不一定要添加通用调用/抄送,但更多限制形式的定界延续是不太可能的。 一个可能的方向可能是代数效应处理程序——我们已经提出了结构化异常处理的建议,并且添加恢复可能是提供结构化“堆栈切换”形式并启用您提到的所有控制抽象的“最简单”方法.

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

jfbastien picture jfbastien  ·  6评论

chicoxyzzy picture chicoxyzzy  ·  5评论

bobOnGitHub picture bobOnGitHub  ·  6评论

arunetm picture arunetm  ·  7评论

konsoletyper picture konsoletyper  ·  6评论