在『Vala 语言中一些好玩的』中介绍了 Vala 语言的外在特征。那么 Vala 的内涵是怎样的?毕竟一门编程语言不能只图外表光鲜,它还得实用。衡量一种编程语言是否实用,主要就是看它的语法表现力以及所编写的程序的运行效率。

衡量语法的表现力常用的指标是代码行数,就是对于同一个计算任务,用不同的语言编写程序,统计每个程序的代码行数。Vala 的语法表现力大致与 C# 相当。据说 Vala 的语法据是模仿 C#(我没接触过 C#,所以我就信了)。事实上,Vala 的语法表现力要比 C# 好一些,因为 C# 没法在类外定义函数,而 Vala 能。Vala 与 C++ 14 的表现力近乎相当,可参考 @依云 的一个编程语言竞赛的项目——SwapView

用 Vala 语言编写的程序,其运行速度介乎 C 与 C++ 之间。2009 年有人做过一些测试(现在 Vala 的速度不会比那时快多少),结果如下:

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 的一些直觉。


garfileo
6k 声望1.9k 粉丝

这里可能不会再更新了。


引用和评论

0 条评论