recover4all注册码(recuva注册码)

前沿拓展:


参数适配器机制不仅复杂,而且成本很高。

本文最初发表于 v8.dev(Faster JavaScript calls),基于 CC 3.0 协议分享,由 InfoQ 翻译并发布。

JavaScript 允许使用与预期形式参数数量不同的实际参数来调用一个函数,也就是传递的实参可以少于或者多于声明的形参数量。前者称为申请不足(under-application),后者称为申请过度(over-application)。

在申请不足的情况下,剩余形式参数会被分配 undefined 值。在申请过度的情况下,可以使用 rest 参数和 arguments 属性访问剩余实参,或者如果它们是多余的可以直接忽略。如今,许多 Web/Node.js 框架都使用这个 JS 特性来接受可选形参,并创建更灵活的 API。

直到最近,V8 都有一种专门的机制来处理参数大小不匹配的情况:这种机制叫做参数适配器框架。不幸的是,参数适配是有性能成本的,但在现代的前端和中间件框架中这种成本往往是必须的。但事实证明,我们可以通过一个巧妙的技巧来拿掉这个多余的框架,简化 V8 代码库并消除几乎所有的开销。

我们可以通过一个**基准测试来计算移除参数适配器框架可以获得的性能收益。

console.time();
function f(x, y, z) {}
for (let i = 0; i < N; i++) {
f(1, 2, 3, 4, 5);
}
console.timeEnd();recover4all注册码(recuva注册码)

移除参数适配器框架的性能收益,通过一个微基准测试来得出。

上图显示,在无 JIT 模式(Ignition)下运行时,开销消失,并且性能提高了 11.2%。使用 TurboFan 时,我们的速度提高了 40%。

这个微基准测试自然是为了最大程度地展现参数适配器框架的影响而设计的。但是,我们也在许多基准测试中看到了显著的改进,例如我们内部的 JSTests/Array 基准测试(7%)和 Octane2(Richards 子项为 4.6%,EarleyBoyer 为 6.1%)。

太长不看版:反转参数

这个项目的重点是移除参数适配器框架,这个框架在访问栈中被调用者的参数时为其提供了一个一致的接口。为此,我们需要反转栈中的参数,并在被调用者框架中添加一个包含实际参数计数的新插槽。下图显示了更改前后的典型框架示例。

recover4all注册码(recuva注册码)

移除参数适配器框架之前和之后的典型 JavaScript 栈框架。

加快 JavaScript 调用

为了讲清楚我们如何加快调用,第一我们来看看 V8 如何执行一个调用,以及参数适配器框架如何工作。

当我们在 JS 中调用一个函数调用时,V8 内部会发生什么呢?用以下 JS 脚本为例:

function add42(x) {
return x + 42;
}
add42(3);recover4all注册码(recuva注册码)

在函数调用期间 V8 内部的执行流程。

Ignition

V8 是一个多层 VM。它的第一层称为 Ignition,是一个具有累加器寄存器的字节码栈机。V8 第一会将代码编译为 Ignition 字节码。上面的调用被编译为以下内容:

0d LdaUndefined ;; Load undefined into the accumulator
26 f9 Star r2 ;; Store it in register r2
13 01 00 LdaGlobal [1] ;; Load global pointed by const 1 (add42)
26 fa Star r1 ;; Store it in register r1
0c 03 Lda**i [3] ;; Load **all integer 3 into the accumulator
26 f8 Star r3 ;; Store it in register r3
5f fa f9 02 CallNoFeedback r1, r2-r3 ;; Invoke call

调用的第一个参数通常称为接收器(receiver)。接收器是 JSFunction 中的 this 对象,并且每个 JS 函数调用都必须有一个 this。CallNoFeedback 的字节码处理器需要使用寄存器列表 r2-r3 中的参数来调用对象 r1。

在深入研究字节码处理器之前,请先注意寄存器在字节码中的编码方式。它们是负的单字节整数:r1 编码为 fa,r2 编码为 f9,r3 编码为 f8。我们可以将任何寄存器 ri 称为 fb – i,实际上正如我们所见,正确的编码是- 2 – kFixedFrameHeaderSize – i。寄存器列表使用第一个寄存器和列表的大小来编码,因此 r2-r3 为 f9 02。

Ignition 中有许多字节码调用处理器。可以在此处查看它们的列表。它们彼此之间略有不同。有些字节码针对 undefined 的接收器调用、属性调用、具有固定数量的参数调用或通用调用进行了优化。在这里我们分析 CallNoFeedback,这是一个通用调用,在该调用中我们不会积累执行过程中的反馈。

这个字节码的处理器非常简单。它是用 CodeStubAssembler 编写的,你可以在此处查看。本质上,它会尾调用一个架构依赖的内置 InterpreterPushArgsThenCall。

这个内置方法实际上是将返回地址弹出到一个临时寄存器中,压入所有参数(包括接收器),第二压回该返回地址。此时,我们不知道被调用者是否是可调用对象,也不知道被调用者期望多少个参数,也就是它的形式参数数量。

recover4all注册码(recuva注册码)

内置 InterpreterPushArgsThenCall 执行后的框架状态。

最终,执行会尾调用到内置的 Call。它会在那里检查目标是否是适当的函数、构造器或任何可调用对象。它还会读取共享 shared function info 结构以获得其形式参数计数。

如果被调用者是一个函数对象,它将对内置的 CallFunction 进行尾部调用,并在其中进行一系列检查,包括是否有 undefined 对象作为接收器。如果我们有一个 undefined 或 null 对象作为接收器,则应根据 ECMA 规范对其修补,以引用全局**对象。

执行随后会对内置的 InvokeFunctionCode 进行尾调用。在没有参数不匹配的情况下,InvokeFunctionCode 只会调用被调用对象中字段 Code 所指向的内容。这可以是一个优化函数,也可以是内置的 InterpreterEntryTrampoline。

如果我们假设要调用的函数尚未优化,则 Ignition trampoline 将设置一个 IntepreterFrame。你可以在此处查看V8 中框架类型的简短摘要。

接下来发生的事情就不用多谈了,我们可以看一个被调用者执行期间的解释器框架快照。

recover4all注册码(recuva注册码)

我们看到框架中有固定数量的插槽:返回地址、前一个框架指针、上下文、我们正在执行的当前函数对象、该函数的字节码数组以及我们当前正在执行的字节码偏移量。最后,我们有一个专用于此函数的寄存器列表(你可以将它们视为函数局部变量)。add42 函数实际上没有任何寄存器,但是调用者具有类似的框架,其中包含 3 个寄存器。

如预期的那样,add42 是一个简单的函数:

25 02 Ldar a0 ;; Load the first argument to the accumulator
40 2a 00 Add**i [42] ;; Add 42 to it
ab Return ;; Return the accumulator

请注意我们在 Ldar(Load Accumulator Register)字节码中编码参数的方式:参数 1(a0)用数字 02 编码。实际上,任何参数的编码规则都是[ai] = 2 + parameter_count – i – 1,接收器[this] = 2 + parameter_count,或者在本例中[this] = 3。此处的参数计数不包括接收器。

现在我们就能理解为什么用这种方式对寄存器和参数进行编码。它们只是表示一个框架指针的偏移量。第二,我们可以用相同的方式处理参数/寄存器的加载和存储。框架指针的最后一个参数偏移量为 2(先前的框架指针和返回地址)。这就解释了编码中的 2。解释器框架的固定部分是 6 个插槽(4 个来自框架指针),因此寄存器零位于偏移量-5 处,也就是 fb,寄存器 1 位于 fa 处。很聪明是吧?

但请注意,为了能够访问参数,该函数必须知道栈中有多少个参数!无论有多少参数,索引 2 都指向最后一个参数!

Return 的字节码处理器将调用内置的 LeaveInterpreterFrame 来完成。该内置函数本质上是从框架中读取函数对象以获取参数计数,弹出当前框架,恢复框架指针,将返回地址保存在一个暂存器中,根据参数计数弹出参数并跳转到暂存器中的地址。

这套流程很棒!但是,当我们调用一个实参数量少于或多于其形参数量的函数时,会发生什么呢?这个聪明的参数/寄存器访问流程将失败,我们该如何在调用结束时清理参数?

参数适配器框架

现在,我们使用更少或更多的实参来调用 add42:

add42();
add42(1, 2, 3);

JS 开发人员会知道,在第一种情况下,x 将被分配 undefined,并且该函数将返回 undefined + 42 = NaN。在第二种情况下,x 将被分配 1,函数将返回 43,其余参数将被忽略。请注意,调用者不知道是否会发生这种情况。即使调用者检查了参数计数,被调用者也可以使用 rest 参数或 arguments 对象访问其他所有参数。实际上,在 sloppy 模式下甚至可以在 add42 外部访问 arguments 对象。

如果我们执行与之前相同的步骤,则将第一调用内置的 InterpreterPushArgsThenCall。它将像这样将参数推入栈:

recover4all注册码(recuva注册码)

内置 InterpreterPushArgsThenCall 执行后的框架状态。

继续与以前相同的过程,我们检查被调用者是否为函数对象,获取其参数计数,并将接收器补到全局**。最终,我们到达了 InvokeFunctionCode。

在这里我们不会跳转到被调用者对象中的 Code。我们检查参数大小和参数计数之间是否存在不匹配,第二跳转到 ArgumentsAdaptorTrampoline。

在这个内置组件中,我们构建了一个额外的框架,也就是臭名昭著的参数适配器框架。这里我不会解释内置组件内部发生了什么,只会向你展示内置组件调用被调用者的 Code 之前的框架状态。请注意,这是一个正确的 x64 call(不是 jmp),在被调用者执行之后,我们将返回到 ArgumentsAdaptorTrampoline。这与进行尾调用的 InvokeFunctionCode 正好相反。

recover4all注册码(recuva注册码)

我们创建了另一个框架,该框架**了所有必需的参数,以便在被调用者框架顶部精确地包含参数的形参计数。它创建了一个被调用者函数的接口,因此后者无需知道参数数量。被调用者将始终能够使用与以前相同的计算结果来访问其参数,即[ai] = 2 + parameter_count – i – 1。

V8 具有一些特殊的内置函数,它们在需要通过 rest 参数或 arguments 对象访问其余参数时能够理解适配器框架。它们始终需要检查被调用者框架顶部的适配器框架类型,第二采取相应措施。

如你所见,我们解决了参数/寄存器访问问题,但是却添加了很多复杂性。需要访问所有参数的内置组件都需要了解并检查适配器框架的存在。不仅如此,我们还需要注意不要访问过时的旧数据。考虑对 add42 的以下更改:

function add42(x) {
x += 42;
return x;
}

现在,字节码数组为:

25 02 Ldar a0 ;; Load the first argument to the accumulator
40 2a 00 Add**i [42] ;; Add 42 to it
26 02 Star a0 ;; Store accumulator in the first argument slot
ab Return ;; Return the accumulator

如你所见,我们现在修改 a0。因此,在调用 add42(1, 2, 3)的情况下,参数适配器框架中的插槽将被修改,但调用者框架仍将包含数字 1。我们需要注意,参数对象正在访问修改后的值,而不是旧值。

从函数返回很简单,只是会很慢。还记得 LeaveInterpreterFrame 做什么吗?它基本上会弹出被调用者框架和参数,直到到达最大形参计数为止。因此,当我们返回参数适配器存根时,栈如下所示:

recover4all注册码(recuva注册码)

被调用者 add42 执行之后的框架状态。

我们需要弹出参数数量,弹出适配器框架,根据实际参数计数弹出所有参数,第二返回到调用者执行。

简单小编综合来说:

Deno 2020 年大事记-InfoQ

关注我并转发此篇文章,即可获得学习资料~若想了解更多,也可移步InfoQ官网,获取InfoQ最新资讯~

拓展知识:

recover4all注册码

http://www.sz1001.net/soft/6407.htm
Recover4all Professional v2.26 注册机

本回答被提问者采纳

recover4all注册码

姓 名:Legal User
注册码:GTNH<BHGBTZKK去下个破解版也得 http://www.ankty.com/soft/1/269/3938.html
希望对您有帮助!

recover4all注册码

直接到这里下载:.cn/n/netdisk/Maildir/root/tools//Recover4all.rar

recover4all注册码

Recover4all Pro 2.23 **注册版
到网上搜一下~~~这个是破解版的

原创文章,作者:九贤生活小编,如若转载,请注明出处:http://www.wangguangwei.com/74695.html