Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

性能比jspatch差5倍的瓶颈在哪里呢? #8

Open
luoyibu opened this issue Jan 5, 2021 · 4 comments
Open

性能比jspatch差5倍的瓶颈在哪里呢? #8

luoyibu opened this issue Jan 5, 2021 · 4 comments

Comments

@luoyibu
Copy link

luoyibu commented Jan 5, 2021

No description provided.

@SilverFruity
Copy link
Owner

SilverFruity commented Jan 5, 2021

本质区别:
OCRunner采用的是解释执行语法树。
JSPatch采用的是JavaScriptCore来实现的,JIT一应俱全,寄存器虚拟机。
随着同一函数或者方法调用次数的增加,性能差距可能会越来越大。
相关资料:
巨佬资料1
大佬资料2

想了很久,却只能这么说。以后了解得更多了,再来添砖加瓦。😂

@luoyibu
Copy link
Author

luoyibu commented Jan 11, 2021

强 编译原理这块还不大熟,我研究下这两个资料

@zyfu0000
Copy link

本质区别:
OCRunner采用的是解释执行语法树。
JSPatch采用的是JavaScriptCore来实现的,JIT一应俱全,寄存器虚拟机。
随着同一函数或者方法调用次数的增加,性能差距可能会越来越大。
相关资料:
巨佬资料1
大佬资料2

想了很久,却只能这么说。以后了解得更多了,再来添砖加瓦。😂

宿主 app 跑 JavaScriptCore 也是没有 JIT 权限的,除非是运行在 wkwebview 里

@SilverFruity
Copy link
Owner

SilverFruity commented Feb 1, 2023

时隔了两年,今天刚好翻到了这个 issue
这里的 demo 就以下列代码为例

long add(long a, long b) {
  return a+b;
}
  1. 解释执行语法树
    以上代码,在 OCRunner 中的执行的伪代码如下:
NSDictionary *scope;
MFValue *a = scope[@“a”];
MFValue *b = scope[@“b”];
MFValue *result = [MFValue longValue];
ORBoxValue value;
switch(a.type) {
  case 'long':
     value.longValue = a.longValue + b.longValue;
     break;
  case ......
  default:
     break;
}
result.boxValue = value;
return result;

以上均是简化后的 OC 代码,其中还有很多的判断逻辑以及方法调用逻辑(FunctionNode -> BlockNode -> BinaryNode) 以及类型判断等等。
上述代码整个调用链路上使用的汇编代码可谓超级庞大,首当其中的则是在 NSDictionary 中使用 key 获取 objecet.
如果我们将一个 arm 指令定义为一个操作数(耗时),那这里将会存在很多的操作数。

  1. 栈虚拟机
    比如 'push args 0' 就是将第一个参数入栈,args 代表的则是一块专属于函数参数的内存区域,0 代表第一个参数
    那这里的栈机指令为
push args 0 // 将 a 压入栈顶,直接通过内存偏移取值
push args 1 // 将 b 压入栈顶
addLong // pop a、b,并将 a + b 的值压入栈顶
ret

相对于语法树的解释执行,这里已经简化了许多

  1. hash_map 取值切换为了 index 取值,这里的效率提升很明显
  2. 在编译时期,确定了 a + b 的类型,以及最终的值类型,直接使用了特化指令 addLong

或许这么说没有太大的体感。
那么可以看看最终还未优化的 arm64 汇编:

mov args x0

load x1, [x0, #0x0]
store x1, [stack_top]
add stack_stop, stack_top, #0x8 // push a

load x2, [x0, #0x8]
store x1, [stack_top]
add stack_stop, stack_top, #0x8 // push b

load x3, [stack_top]
sub stack_stop, stack_top, #0x8 // pop a

load x4, [stack_top]
sub stack_stop, stack_top, #0x8 // pop b

add x5, x3, x4  //  a +b
store x5, [stack_top]
add stack_stop, stack_top, #0x8 // push result
// 栈帧回溯操作 2-3 个指令
...

至此,我认为解释执行计算密集型的任务性能会有 10 倍差距也属于正常

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants