在『Vala 语言中一些好玩的』中介绍了 Vala 语言的外在特征。那么 Vala 的内涵是怎样的?毕竟一门编程语言不能只图外表光鲜,它还得实用。衡量一种编程语言是否实用,主要就是看它的语法表现力以及所编写的程序的运行效率。
衡量语法的表现力常用的指标是代码行数,就是对于同一个计算任务,用不同的语言编写程序,统计每个程序的代码行数。Vala 的语法表现力大致与 C# 相当。据说 Vala 的语法据是模仿 C#(我没接触过 C#,所以我就信了)。事实上,Vala 的语法表现力要比 C# 好一些,因为 C# 没法在类外定义函数,而 Vala 能。Vala 与 C++ 14 的表现力近乎相当,可参考 @依云 的一个编程语言竞赛的项目——SwapView。
用 Vala 语言编写的程序,其运行速度介乎 C 与 C++ 之间。2009 年有人做过一些测试(现在 Vala 的速度不会比那时快多少),结果如下:
在 SwapView 项目中,Vala 程序的的运行速度比 C/C++ 慢了近乎一倍。这是可以解释的,因为 SwapView 项目中的 Vala 程序在操作文件时,用了 GIO 库中经过重重封装的文件处理机制,而不是 C 原生的那个。
Vala 程序运行时消耗的内存会高于 C/C++ 程序。因为 Vala 代码被编译到 C 代码时,会发生很大幅度的代码膨胀。由 Vala 源代码生成的 C 代码行数差不多是 Vala 源码的 10 倍,而等效的 C 代码最小行数只是 Vala 源码的 2 倍。Vala 编译代码的膨胀之处主要在于 Vala 编译器所生成的大量栈数据,这种不节制,这自然会增大运行时的内存消耗。例如,下面的 Vala 代码:
List<SwapView?> getSwap(){
List<SwapView?> ret = new List<SwapView?>();
try{
FileEnumerator enumerator = File.new_for_path("/proc/").enumerate_children("standard::*", FileQueryInfoFlags.NONE);
FileInfo fpid;
while((fpid = enumerator.next_file()) != null){
int64 pid = 0;
if(fpid.get_file_type () == FileType.DIRECTORY &&
int64.try_parse(fpid.get_name(), out pid)){
SwapView s = getSwapFor(pid);
if(s.size > 0)
ret.append(s);
}
}
}catch(Error e){} // do nothing for errors
ret.sort((a, b) => (a.size < b.size) ? -1 : (a.size > b.size) ? 1 : 0);
return ret;
}
Vala 编译器会将其翻译为下面的 C 代码:
GList* getSwap (void) {
GList* result = NULL;
GList* ret = NULL;
GError * _inner_error_ = NULL;
ret = NULL;
{
GFileEnumerator* enumerator = NULL;
GFile* _tmp0_ = NULL;
GFile* _tmp1_ = NULL;
GFileEnumerator* _tmp2_ = NULL;
GFileEnumerator* _tmp3_ = NULL;
GFileInfo* fpid = NULL;
_tmp0_ = g_file_new_for_path ("/proc/");
_tmp1_ = _tmp0_;
_tmp2_ = g_file_enumerate_children (_tmp1_, "standard::*", G_FILE_QUERY_INFO_NONE, NULL, &_inner_error_);
_tmp3_ = _tmp2_;
_g_object_unref0 (_tmp1_);
enumerator = _tmp3_;
if (G_UNLIKELY (_inner_error_ != NULL)) {
goto __catch1_g_error;
}
while (TRUE) {
GFileInfo* _tmp4_ = NULL;
GFileEnumerator* _tmp5_ = NULL;
GFileInfo* _tmp6_ = NULL;
GFileInfo* _tmp7_ = NULL;
GFileInfo* _tmp8_ = NULL;
gint64 pid = 0LL;
gboolean _tmp9_ = FALSE;
GFileInfo* _tmp10_ = NULL;
GFileType _tmp11_ = 0;
_tmp5_ = enumerator;
_tmp6_ = g_file_enumerator_next_file (_tmp5_, NULL, &_inner_error_);
_tmp4_ = _tmp6_;
if (G_UNLIKELY (_inner_error_ != NULL)) {
_g_object_unref0 (fpid);
_g_object_unref0 (enumerator);
goto __catch1_g_error;
}
_tmp7_ = _tmp4_;
_tmp4_ = NULL;
_g_object_unref0 (fpid);
fpid = _tmp7_;
_tmp8_ = fpid;
if (!(_tmp8_ != NULL)) {
_g_object_unref0 (_tmp4_);
break;
}
pid = (gint64) 0;
_tmp10_ = fpid;
_tmp11_ = g_file_info_get_file_type (_tmp10_);
if (_tmp11_ == G_FILE_TYPE_DIRECTORY) {
GFileInfo* _tmp12_ = NULL;
const gchar* _tmp13_ = NULL;
gint64 _tmp14_ = 0LL;
gboolean _tmp15_ = FALSE;
_tmp12_ = fpid;
_tmp13_ = g_file_info_get_name (_tmp12_);
_tmp15_ = int64_try_parse (_tmp13_, &_tmp14_);
pid = _tmp14_;
_tmp9_ = _tmp15_;
} else {
_tmp9_ = FALSE;
}
if (_tmp9_) {
SwapView s = {0};
gint64 _tmp16_ = 0LL;
SwapView _tmp17_ = {0};
SwapView _tmp18_ = {0};
gdouble _tmp19_ = 0.0;
_tmp16_ = pid;
getSwapFor (_tmp16_, &_tmp17_);
s = _tmp17_;
_tmp18_ = s;
_tmp19_ = _tmp18_.size;
if (_tmp19_ > ((gdouble) 0)) {
SwapView _tmp20_ = {0};
SwapView _tmp21_ = {0};
SwapView* _tmp22_ = NULL;
_tmp20_ = s;
_tmp21_ = _tmp20_;
_tmp22_ = _swap_view_dup0 (&_tmp21_);
ret = g_list_append (ret, _tmp22_);
}
swap_view_destroy (&s);
}
_g_object_unref0 (_tmp4_);
}
_g_object_unref0 (fpid);
_g_object_unref0 (enumerator);
}
goto __finally1;
__catch1_g_error:
{
GError* e = NULL;
e = _inner_error_;
_inner_error_ = NULL;
_g_error_free0 (e);
}
__finally1:
if (G_UNLIKELY (_inner_error_ != NULL)) {
__g_list_free__swap_view_free0_0 (ret);
g_critical ("file %s: line %d: uncaught error: %s (%s, %d)", __FILE__, __LINE__, _inner_error_->message, g_quark_to_string (_inner_error_->domain), _inner_error_->code);
g_clear_error (&_inner_error_);
return NULL;
}
ret = g_list_sort (ret, ___lambda4__gcompare_func);
result = ret;
return result;
}
观察上述高度膨胀的 C 代码,显然 Vala 编译器还需要大幅度的优化,否则就真的是金玉其外,败絮其中了。
我不打算用 Vala 来写实际的程序。最近之所以观摩 Vala,主要是为了之前曾经挖的一个坑——为 GObject 重写一份指南。在做这件事时,我打算将 Vala 作为 GObject 代码的『注释』,从而能够更好的建立对 GObject 的一些直觉。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。