近日,MIT CSAIL 实验室正式发布了 Julia 1.0,不少人称,该语言结合了C语言的性能和Python 的易上手性,被称为最聪明的一群大脑创造出的现代编程语言。
Julia 官网:
https://julialang.org/
Julia Github地址:
https://github.com/JuliaLang
自诞生之日起,Julia语言就备受关注。但是,这门“未来的语言”真的值得所有人学习吗?
在Julia初诞生之初,一位来自Facebook的软件工程师Victor Zverovich对这门语言做了一个性能测评,他从性能、语言、安全性、Library、发展上详细叙述了Julia的特质,最终得出了“give up on Julia”的结论。
尽管这份“买家秀”距离今天时间略久,Julia经过一年多的演进,在本周发布时已有了很大的进步,但是一门语言自诞生之日,其基因毕竟已经携带了某种特性,因此这篇文章现在读起来仍然有一些不错的参考价值。
作者Victor Zverovich个人主页:
http://www.zverovich.net/
当然,作为一门正在被追捧的语言,Julia还是值得了解。以下两个视频可以帮你迅速了解并安装尝试一下这门语言。
第一个视频向python使用者详细介绍了Julia的性能、特征。演讲来自IBM论坛,演讲者有1.5年Julia使用经验,也是杜克大学技术神经网络实验室研究员,感兴趣的同学可以戳视频观看
在官方发布后,也有人制作了一个详细的Julia安装使用手把手教学视频
最后,Victor Zverovich的这份Julia差评买家秀,可能可以部分缓解你的编程语言焦虑。
文章链接:
http://www.zverovich.net/2016/05/13/giving-up-on-julia.html
首次了解Julia编程语言时,我对它非常热衷。Julia语言的吸引力从官网上的功能列表就可见一斑:
多分派:提供跨多种参数类型来组合定义函数的能力
动态类型系统:文档,优化和分派的类型
良好的性能,甚至能接近包括C语言在内的静态编译语言
内置程序包管理器
类似Lisp的宏和其他元编程工具
可以通过使用PyCall包来调用Python函数
不需要包装器或特殊API就能直接调用C函数:
强大的类似shell的功能,用于管理其他进程
专为并行和分布式计算而设计
协同程序:轻量级“绿色”线程
用户自定义的类型与内置函数一样快速、紧凑
为不同的参数类型自动生成高效的专用代码
针对数字和其他类型的优雅且可扩展的转换
对Unicode的有效支持,包括但不限于UTF-8
MIT 开源许可(MIT license):免费+开源
然而,随着我对Julia语言的了解变得深入,亲自上手实验,我对它越来越不满意了。我将在这篇文章中解释为什么。
性能
性能
当我看到Julia网站上报道的微基准测试时,我感受到了第一个次失望。粗浅浏览就能感受到其中的大问题
链接:
https://github.com/JuliaLang/julia/issues/4662
Julia比C或C++慢得多,这一点倒是没有什么可指责的,毕竟大多数语言都是。最令人失望的是其声称的表现与观察到的表现之间的显著差异。
例如,Julia中的一个简单的hello world程序运行速度比Python的版本慢约27x,比C语言慢约187x。这些性能是Linux上运行的结果,虽然我自己不使用Windows,但别人告诉我在那个环境下的差异更明显。
# test.jl println("Hello, World!")
# test.py print("Hello, World!")
#include <stdio.h> int main() { printf("Hello, World!\n"); }
$ time julia test.jl Hello, World! real 0m0.371s user 0m0.240s sys 0m0.312s $ time python test.py Hello, World! real 0m0.014s user 0m0.004s sys 0m0.004s $ gcc -O3 test.c $ time ./a.out Hello, World! real 0m0.002s user 0m0.000s sys 0m0.000s
如果忽略启动时间,Julia在简单的数组运算、矩阵运算以及循环上性能尚佳,但我们已经知道怎么用Python或者其他语言来高效执行这些操作了。👇
https://www.ibm.com/developerworks/community/blogs/jfp/entry/Python_Meets_Julia_Micro_Performance
不仅仅是脚本,Julia的REPL的响应性优化需要很长时间才能启动,并且在使用JIT编译器(Just-in-Time Complier)时有明显滞后。更让人担忧的是,这一问题的解决上似乎没有太大进展。一年前使用时,REPL就是个大痛点,现在仍然如此。
除此之外,Julia程序的内存消耗过多。Julia上面hello world示例使用的内存比Python高18倍,比C高92倍。
可能的原因是Julia使用LLVM进行JIT编译。LLVM非常适合作为静态编译语言的编译器后端,但众所周知,它在动态语言环境中无法达到同样的效果。Unladen Swallow和最近从WebVM迁移的WebKit 就是值得注意的例子。👇
https://en.wikipedia.org/wiki/Unladen_Swallow
https://webkit.org/blog/5852/introducing-the-b3-jit-compiler/
语言
在语言设计中,Julia也保留了一些现代编程语言很好的功能,但其语法仍是一大诟病。颇为模糊的语法大概只有MATLAB的忠粉会习惯。
当然,在可读性方面,Julia也很难与Python竞争。人们常说,代码被读比被写的次数多。从这一点来看,Julia肯定有改进的余地。表达式语法更合理了,但仍然有一些非正统的选择操作符,标点符号和多行注释的语法(例如#= \ // $?),这些注释会给使用者带来不少麻烦。
从1开始索引的设计决策也有待商榷。虽然在某些情况下它可能很方便,但在与所有使用基于0索引的主流编程语言进行互操作时,会增加错误的出现,也会增加额外工作。例如:
ret = @grb_ccall(delconstrs, Cint, ( Ptr{Void}, Cint, Ptr{Cint}), model, convert(Cint,numdel), idx.-1)
当然,有人可能会认为Julia并不是一种通用编程语言,而是一种数值计算语言。但是,正如现在的Python所展现的能力,编程语言并不必为了一个性能而在另一个性能上有所欠缺。
我想在本节中提到的最后一个问题是API文档。甚至与Doxygen相比,Julia标准文档系统都算得上退步,更不用说Sphinx了。它不依赖于语义标记,而采用基于Markdown的基本格式,更侧重于展示。除了明显的Markdown限制之外,这种格式也让编写异构项目的开发文档更麻烦了。
安全
安全
当然,JNA-和ctypes类型的FFI确实很方便。然而,将其设置为与本机API接口的默认方式就会出现重大安全问题。C和C++都采用头文件是有其原因的。手动声明所有内容不仅耗时,而且容易出错。在C Call 上出一点点问题都可能造成段错误(segmentation fault)。有意思的是,当因为官方示例有问题,我把文档中的代码从libc更改为libc.so.6,就出现了段错误。
julia> val = ccall((:getenv, "libc.so.6"), Ptr{UInt8}, (Ptr{UInt8},), var) signal (11): Segmentation fault
这个性能上,Python甚至Java与C的API都更好用。
Libraries
Libraries
Julia目前缺乏的另一个重要内容是库(Library),尤其是标准库。
例如,文本格式(.txt)是人们可能想到的最基本和最常用的语言形式之一,而Julia甚至落后于C++ 98。标准库提供了@printf和@sprintf,但这些都是无法扩展的。你甚至无法将它们对复数进行格式化。Julia确实有一个基本字符串插值,但目前来看,它似乎只对最基本的格式有用。
作为宏,@printf/ @sprintf为每个格式字符串生成自定义代码,希望它比在运行时解析它更有效。有人甚至说,Julia语言“要赶上C语言还需要一段时间,更不用说击败它了”👇。
http://stackoverflow.com/q/19783030/471164
举个例子,让我们看一下C中的一个简单的printf示例:
void f(char *buffer, const char *a, double b) { sprintf(buffer, "this is a %s %g", a, b); }
它只汇编了几条指令:
f: .LFB23: .cfi_startproc movq %rsi, %r8 movl $.LC0, %ecx movq $-1, %rdx movl $1, %esi movl $1, %eax jmp __sprintf_chk .cfi_endproc
下面是julia版本:
function f(a, b) @sprintf("this is a %s %g", a, b) end
运行code_native(f, (ASCIIString, Float64))并查看输出,您将看到将近500条指令。可以想象一下@(s)printf调用的次数。这个数量是相当巨大的。由于经常使用文本格式,这种指令方式可能会产生严重的代码冗长问题。C语言版本可能没有这么安全,但有一些安全的替代方案,也并不会牺牲代码的紧凑性。
用于单元测试的库也非常基础,至少与C++和Java中的库相比是这样。
发展
发展
我对大型代码库并不陌生,但在考虑是否为Julia项目做开源贡献时,我发现代码库简直就是C,C++,Julia和Lisp的混搭,不得不望而却步,尽管我对LLVM在后端的使用有一些经验。原因并不是因为不喜欢混搭中的某一种语言,而是该项目要求开发人员同时精通数种编程语言。这相当于只有拥有独特专业知识或在特定领域内工作的开发者才有能力参与到项目中。正如Dan Luu所说:
Julia开发组是一个由才华横溢的人员构成的小团队。他们基本上可以将所有代码保存在各自大脑中,也可以取得很大的进步,然而代价是其他开发者更难以做出贡献。这值得吗?很难说。
我不确定这种方法是否可取,其他人报告Julia开发速度放缓 :
Julia的发展的确越来越慢。在过去的9个月里,我都没怎么见过它。
总结
总而言之,目前的Julia语言有如下问题尚待改进:
性能问题,包括启动时间长和JIT编译的延迟
与其他语言的互操作性问题,
文本格式化工具不足,
缺乏良好的单元测试框架,
默认情况下不安全的本地API接口,
不必要的复杂代码库,
对bug修复的不够重视
尽管如此,我认为该语言可以作为matlab的开源替代品,因为这门编程语言的语法可能对matlab用户很有吸引力,甚至会撼动Python在数值计算的地位。