从零开始手写WebMVC框架 - 前言

Java出身的同学们,你们是不是吐嘈过,或者被吐嘈过Java/Java程序员的种种不堪呢?

启动狂慢!改个配置重启几十秒!

框架一层套一层!原理看不见摸不着!

随便一个库依赖一堆好几十兆!

毕业就搞SSH,n年了还在搞SSH!!!简直就是搬砖的码农

XML反人类!

虽然作为已经20年的老龄语言,Java也存在着诸如亲妈不给力,设计模式超过业务代码,xml反人类等等等等的缺点……

不过Java真的是如此不堪么?Java就必须臃肿起来?SSH真的那么深不可测?

让我们来用现实说话,在接下来的几周时间,0基础,一起写一个超轻量级别的MVC ;)

查看完整版...

高可移植解释型虚拟机优化专场·第一章·补

平媒用稿,请勿转载

咳咳,本来应该直接突入下一章,突然想起来stackMap的一些小问题,在这里补充一下说明

  1. 遇到旧版的jar包怎么办?
    当然旧版的jar包是是不可能给你stackMap的,甚至也不会给你内联finally中的代码块。这个时候,用proguard工具preverify一下,就有了。很多之前开发过j2me的同学可能对这个眼熟,对,本身stackMap就是从j2me里面拿过来的好用的东西。
  2. 遇到一些旧代码直接生成的字节码怎么办?
    很多旧版的字节码生成工具生成的代码中,(包括但不限于旧版的asm .js,啊不,没.js,甚至包括Harmony自带的Proxy实现),没有放进去stackMap(当然也不会内联jsr)。sun对这种情况的推荐是保留原来的复杂的逻辑,从0开始计算stackMap,不过实在是过于复杂。我个人虽然推荐抛弃掉这些时代的眼泪,不过Harmony已经被apache废弃了,指望他们更新是不可能了,而Harmony目前是我看到的唯一的可以让你随便折腾里面的代码还不用开源runtime库,所以只能忍痛考虑一下怎么办。不过幸好Harmony的Proxy实现生成的代码中一不包含跳转二不包含jsr,因此就这么着了。。。
  3. 都做到这个地步了为啥不JIT?
    简单地说……我不会orz。复杂点说,首先,JIT这种东西需要动态生成可执行的代码,这部分代码在不同平台上是不一样的,而且动态生成的代码在iOS下根本无法执行(Android下倒是无所谓)所以干脆就不动这个念头了;其次,JIT并不单纯是一个一下子“叮”就能解决一切性能问题的简单方案,java自带的JIT之所以能跑到接近C语言一半的性能,主要是因为java在JIT的时候重新对字节码进行了优化,可以认为是基于profile用-O3甚至是-lto进行了全面的重编译,一般的JIT也是作不到的

那么言归正传,在应用stackMap,废弃掉typeStack之后,Linpack测试立马从25flops涨到47flops,涨幅接近60%。那么还能不能更快?

这个还得且听下回分解~

目录

高可移植解释型虚拟机优化专场·开场篇
高可移植解释型虚拟机优化专场·第一章 stackMap的解析和优化
高可移植解释型虚拟机优化专场·第一章·补

高可移植解释型虚拟机优化专场·第一章

平媒用稿,请勿转载

堆栈数据冗余

由于GC的需要,虚拟机需要知道每个线程堆栈里面每个元素放的是java对象还是一个普通的数字,因此在之前的设计中设计了两个堆栈,一个stack存放具体的数字,一个typeStack存放类型,每次push的时候,我们都会同时写typeStack,带来两个问题

  1. 多了一次写入
  2. 为了降低typeStack浪费的空间,之前使用了bitset来压缩typeStack,typeStack中每一位代表一个位置的stack是否为对象。因此写入的性能变差了
  3. 另外,在目前的内存结构中,typeStack是整体放在stack后面的,stack的长度是64k,因此连续写入的时候这两个位置还不在一页上

因此,首先尝试把这个给typeStack优化掉。

条条大路通罗马,虽然到达某个指令所经过的分支等等都有所不同,但是java的编译器保证了java到达每条指令时的栈状态是确定的,也就是说,无论之前走了怎样的分支,相同的位置的指令执行之前的栈状态(栈的大小,每个单元的类型)是确定的。这样我们就可以在运行之前先计算出每条指令的栈状态,存起来,GC的时候再根据当前指令获取即可。

那么如何计算每个指令之前的栈状态呢?第一条指令之前当然是根据参数来,后面的指令只要用动态规划算法把所有指令都执行一遍即可。不过如果你写了try{}finally{}这种代码,finally中的代码在java6之前是作为方法内的子程序存放的在不同的场合(如正常执行完,抛异常,return)通过jsr调用finally那部分,而通过不同的途径进入finally中的代码,栈的内容是不一样的,如果finally中再嵌套finally,进入的状态就有四种,因此在这种情况,java6之前的jvm规范推荐了一种复杂的办法把能到达这条指令的所有jsr作为key,几种情况分开保存。

而在java6之后,虚拟机规范通过两种方式简化了这个流程:

  1. 废弃了jsr/ret指令对,编译器对于finally中的代码,有几种调用到的情况就克隆出几份来
  2. 在method中增加了一种叫stackMap的属性,里面通过增量的方式保存了某些指令执行前的栈状态。这些指令涵盖了所有的跳转点。因此,如果需要知道某条指令之前的栈状态,只需要找到最近的stackMap帧,向后一条一条地按照指令对栈的作用计算栈的状态即可。

在应用stackMap,废弃掉typeStack之后,Linpack测试立马从25flops涨到47flops,涨幅接近60%。那么还能不能更快?

且听下回分解~

目录

高可移植解释型虚拟机优化专场·开场篇
高可移植解释型虚拟机优化专场·第一章 stackMap的解析和优化
高可移植解释型虚拟机优化专场·第一章·补

高可移植解释型虚拟机优化专场·开场篇

平媒用稿,请勿转载

前言

年前的业余时间,对脚本娘fiscevm的c语言版进行了大量优化工作,在使用了1.6新增的关于stackMap部分内容对typeStack进行优化,以及对指令的Dispatcher造成的分支预测失败次数进行优化之后,脚本娘的速度提升了130%,现在已经接近java -Xint的90%了,在我的i7 macbook air上,用runtime里的com/cirnoworks/fisce/privat/Linpack来测试,旧版fiscevm是30Mflops/s左右,新版是65Mflops左右,用-Xint关掉HotSpot的java是80Mflops/s左右。当然,打开HotSpot的话,java是1300Mflops/s。不jit的虚拟机再怎么着也赶不上jit的虚拟机啊

随想

想想从脚本娘诞生开始,就一直处在闭门造车的阶段,当时要是经常抬头看看他山之石就好了。也因此,在借助了很多他山之石之后,我也想利用接下来几篇的时间,把脚本娘成长的经历,也就是对解释型虚拟机的性能优化历程分享给大家,敬请期待。

Chaptor 0 在开始之前

代码

作为对比的基准,优化前的代码在这里下载:fiscevm-c-master.tar.gz

优化后的版本在这里下载:fiscevm-c-vmgen.tar.gz

术语

  1. thread: 当前的线程 里面包括了一系列的frames,还有锁等等相关的信息
  2. frame: 当前的执行上下文,比如指令的位置pc,栈顶指针sp等等,在thread中以堆栈的形式堆放,
  3. sp: 栈顶指针
  4. pc: 指令指针
  5. instructions: 当前方法的指令列表,是个数组,instructions[ip]就是当前的指令
  6. 剩下的名称在第一次使用的时候会提供解释

基本结构

堆栈(Stack)

堆栈是一块

目录

高可移植解释型虚拟机优化专场·开场篇
高可移植解释型虚拟机优化专场·第一章 stackMap的解析和优化
高可移植解释型虚拟机优化专场·第一章·补

Markdown泥好~新架构诞生

虽然blog已经好久没写了,不过果然有的时候还是想写点儿东西,一来有些想法不记录一下就慢慢消失了,二来也想写点儿什么东西发到朋友圈不是么(喂

之前写blog一个很大的障碍就是咱那个破虚拟主机实在是太慢了,开个wordpress后台都要半天,打开就不想写了(汗,明明就是懒

于是干脆一不做二不休,把整个博客都搞成静态的得了,于是用jekyll重写搭了一个静态的博客系统。在本地随便开个文本编辑器写po,图片直接拷到目录里,用git推到服务器上即可,轻松愉快。服务器会在不久之后自动build完之后发布,发布出来完全是静态的,除了用nginx作gzip压缩之外完全没有服务器运算,实际测算页面响应时间从原来的2秒左右瞬间下降到了几百毫秒,实在是太赞了w

主题也是基于bootstrap和一些html5的现代插件重新堆的,本来想激进地搞全站ajax化,想了想还是算了。。。现在的页面把样式全部抽出到css里,再gzip一下之后也就没多大了,就算用手机来看也不会走太多流量

目前还差一个首页,一个基于分类的汇总页面,还有一个导航条,不过先上线把,于是部落格正式诞生!wwwww

至于服务器,换到腐国了,发现腐国的服务器可能是因为时差的缘故,白天快,晚上慢……正好适合上班收听作业曲……啊不,是撰写技术心得用 =_,=