之前做了一个转Excel表到lua/二进制/json/xml的工具-xresloader。目的一方面是方便策划。另一方面是统一客户端和服务器的转表模式,并且要灵活适应环境变化。

最初做的时候考虑到既要方便Windows下策划和前端使用,又要方便后台部署在服务器上使用,甚至要集成在一些自动化的系统里。所以必须要跨平台。

并且需要对Excel操作比较好的Lib和对protobuf等各种协议操作比较方便的Lib。

所以综合考虑以后用了java来实现。

但是实际使用过程中,随着表格的数量越来越多,转换时间也越发地不可忍受。在做了简单地分析以后发现,在转换一个表格的时候,java载入jar包之后花了超过三分之二的CPU用于编译和编译优化java字节码。不到三分之一的CPU时间用于转表。而一次编译java字节码花费的时间要大约1秒钟左右。 这就意味着我们目前的70个表要花1分半中的时间编译java字节码,而实际转表只要不到半分钟。这果断是极大的CPU浪费并且等待时间太长了。

java的这个JIT功能对服务器程序是非常有用的,因为这样可以在编译期不需要像C++一样花费大量的时间,并且容易做跨平台的指令集抽象。实际开发中编译很频繁,java这个特性可以减少大量的开发中等待时间,而上线以后又能有比较好的运行效率。 但是这个特性放到命令行工具上就比较伤了,因为命令行工具一般都是执行次数频繁但是执行时间很短的,如果像这样每次运行去编译反而会浪费总体的资源和时间。最理想的情况是和_.Net Native_一样可以把它便以为本地应用。可惜java貌似还没这个功能。

而我尝试关掉java的JIT时,实际时间会更长,所以就有必要针对Java这个特性做一些特别的优化。 仍然是为了容易和其他工具集成,所以我这里设计成了可以通过stdin来获取多次转表的信息。还有一点就是要把单例的部分在下一次转换的时候清理掉。 然后批量转表工具**xresconv-clixresconv-gui都接入新的转换方式。通过管道来控制子进程的标准输入,以此来控制转表内容。 实际优化后的效果相当明显。虽然我懒得把对比数据跑出来了,但是原先70个表每个需要超过1秒钟,4个并发任务总时间也会需要十几二十秒**,而现在2个并发任务的情况下,总转换时间6秒左右。 现在的转换步骤里,转第一个表的时候大约需要1秒钟,然后剩下的前十个左右大约在0.2-0.4秒钟。再后面的速度就是”刷得一下“就结束了。 可以很明显地感受到java JIT的第一次编译优化,运行频繁以后的第二次更深度优化带来的性能提升。后面一批转表感觉上速度提升有_十倍_以上。

然后我测了一下2个并发任务和4个并发任务时的区别,2个并发任务的情况下更能发挥java的JIT优势,耗时6秒左右。而4个并发任务时虽然进程数更多,但是JIT优化的效果会降低很多,反而总时间在9秒左右。 而单线程时JIT效果最好,但是总时间感觉比2个并发任务略慢一点点。(我的机器是4核8线程的CPU,型号是至强 E3-1230 V2)

所以我把批量转表工具的默认最大并发度都限制到了2。 总的来说,这次的优化效果还是很明显的,虽然在批量转表的情况下还有一些优化空间(比如macro表几乎不变,可以缓存下来),下次有空再说吧。