最近用julia写一个处理一批csv文本文件做一些统计生成表格的程序,感觉julia目前最大的问题是加载包特别慢,using DataFrames和CSV等一些包几乎就能用10s左右,即使用PackageCompiler编译后也要3s,而且第一次跑CSV读取到DataFrame也要5s,以后每次只要0.001s以下,计算部分第二次运行也只用0.1s以下(总体用时可以到30s以上)。所以现在用Genie写一个web服务启动一个julia进程一直跑着,用golang撸个命令行程序去发送http请求调用。不知道大家有没有更好的做法?
我现在是用 Jupyter 加 IJulia 来挂着 julia 进程的,可以显示图片,不过自动化或者嵌入到其他程序中可能会麻烦点。
Roger
3
你遇到这个问题的根本原因在多个module的交叉编译结果没有进行cache,目前没有特别好的方案。未来应该会在各个环境里再提供全局cache就会改善这个问题。但是依然是有workaround的方法的。
原理上我们希望把一些我们要用的generic 函数和他们的specific type的组合给编译掉,然后存下来。所以我们可以对一个已经开发完成的应用进行tracing,获得我们需要编译的类型和方法信息。这利用到
SnoopeCompile可以用来自动产生precompile的指令,这样就可以把大部分实际上会用的函数给静态预编译出来。
然后我们希望能够基于这个方法再编译出一个静态的链接库储存这些编译结果,这个方法叫Fezzik,它会运行一遍这些函数做trace,然后获得类型信息和预编译指令,最后给你编译出一个sysimg的链接库。
不过如果是应用使用ApplicationBuilder可能更好,需要就你自己的情况试一下:
然后我预计年底或者明年在下个版本里会有编译器自带的静态编译功能:
应该会大幅改善这个情况,此外1.2版本也有很多compile latency方面的改进,八月初就可以用上。或者也可以用当前的release candidate尝鲜。
3 个赞
ApplicationBuilder 和 PackageCompiler 是不是差不多,编译出来速度也一样
Roger
5
PackageCompiler是一个静态编译器,它在编译出来的结果第一次运行速度快不快取决于你的预编译指令是否覆盖完全,因为Julia本质上是一个动态语言,所以一般来说类型信息要到运行时才能确定。Fezzik也是依赖于PackageCompile的,但是不同之处在Fezzik会进行自动的tracing,也就是把程序运行一遍然后产生编译指令,这可以帮助编译器知道具体要编译的concrete type都有什么。否则理论上是无法编译的(因为generic函数的编译可能有无数多种)。
要是Julia能对编译的结果进行cache就好了。这样不要头次运行函数都要编译一次,比如zeros这些常用函数,历史上都不知道被执行了多少遍了。
上述带来的问题,就是调试代码时候不爽,改一点代码,就要重新include,重新编译。
Roger
8
有正在讨论中的environment cache,现在的主要问题是module A的函数module B的类型编译出来的不知道cache算谁的(不算A的也不算B的),所以方案是cache到environment全局里去。我预计会改善这个情况很多。不过1.2和1.3的编译速度已经提高了很多了,所以一些情况下不cache,延迟也不那么明显了。