Matlab中用类封装函数

作者:

上次大规模使用 Matlab 还是本科的时候,当时还是 5.3 版,现在重新尝试它,已经是 7.8 ( R2009a ),而且 R2010b 版都已经发售。而这些版本引入的一个新玩意儿便是面向对象化编程( object-oriented programming , OOP )。

使用类( class )有很多好处,其中一个重要的好处便是解决变量名冲突和让函数、对象的结构清晰。class 的 static function 可以在不定义类的实例直接调用类的成员函数,比如定义

classdef tools < handle
    methods (Static = true)
        function a = test(b, c)
            a = b + c;
        end
    end
end

然后可以直接通过

a = tools.test(b, c);

调用 test 函数。这样做的好处是解决 test 的变量名冲突问题,而且可以将类似的函数封装到一块,使得函数结构显示层次化。

但这样做是有代价的,其中一个便是效率问题,假设还有一个完全一样的用普通 m 文件定义的 test 函数,下面两段代码测试了这两者各运行一百万次所需的时间:

tic
for i = 1:1000000, test(i, i); end
toc

tic
for i = 1:1000000, tools.test(i, i); end
toc

那么普通函数的百万次调用的时间开销约 0.25 秒,而封装后的函数的百万次调用的时间开销高达 14.5 秒,相差约 70 倍。而且如果通过 class 的实例来调用,所需要的时间更长,下面这段代码显示百万次调用需要 22.5 秒。

t = tools;
tic
for i = 1:1000000, t.test(i, i); end
toc

所以到底该不该封装,还需取决于实际情况,如果函数本身特别简单,并且会被循环调用,最好还是通过 m 文件函数的形式。之前 MIT 大牛给出了更多地建议

  1. 虽然 for-loop 的速度有了很大改善, vectorization (向量化)仍旧是改善效率的重要途径,尤其是在能把运算改写成矩阵乘法的情况下,改善尤为显著。
  2. 在不少情况下, for-loop 本身已经不构成太大问题,尤其是当循环体本身需要较多的计算的时候。这个时候,改善概率的关键在于改善循环体本身而不是去掉 for-loop。
  3. MATLAB 的函数调用过程(非 built-in function )有显著开销,因此,在效率要求较高的代码中,应该尽可能采用扁平的调用结构,也就是在保持代码清晰和可维护的情况下,尽量直接写表达式和利用 built-in function ,避免不必要的自定义函数调用过程。在次数很多的循环体内(包括在 cellfun, arrayfun 等实际上蕴含循环的函数)形成长调用链,会带来很大的开销。
  4. 在调用函数时,首选 built-in function ,然后是普通的 m-file 函数,然后才是 function handle 或者 anonymous function。在使用 function handle 或者 anonymous function 作为参数传递时,如果该函数被调用多次,最好先用一个变量接住,再传入该变量。这样,可以有效避免重复的解析过程。
  5. 在可能的情况下,使用 numeric array 或者 struct array ,它们的效率大幅度高于 cell array (几十倍甚至更多)。对于 struct ,尽可能使用普通的域(字段, field )访问方式,在非效率关键,执行次数较少,而灵活性要求较高的代码中,可以考虑使用动态名称的域访问。
  6. 虽然 object-oriented 从软件工程的角度更为优胜,而且 object 的使用很多时候很方便,但是 MATLAB 目前对于 OO 的实现效率很低,在效率关键的代码中应该慎用 objects。
  7. 如果需要设计类,应该尽可能采用普通的 property ,而避免灵活但是效率很低的 dependent property。如非确实必要,避免重载 subsref 和 subsasgn 函数,因为这会全面接管对于 object 的接口调用,往往会带来非常巨大的开销(成千上万倍的减慢),甚至使得本来几乎不是问题的代码成为性能瓶颈。

Q. E. D.

类似文章:
没有人敢说 Matlab 的帮助文件不够详细和全面,但 Matlab 里面的确存在一些功能和函数,你很难找到它的帮助文档。下面列举一些,以后碰到其它的也会放到这里来:
上次说起 遇到两个 Matlab 的效率问题 ,还剩下 save , load 没说。不过这个问题的结论挺简单的,就是在 save 的时候尽量指定使用-v6 选项。
最近用 Matlab 写了个东西,运行效率不如我意。用 Profiler 跟踪了一下,结果让我大吃一惊。其中三分之一的时间花费在 datestr 这个函数上,六分之一的时间花费在 save 和 load 数据上。这里先谈前一个问题,关于 save 和 load 的效率以后再谈。
本文结论: 不要过度担心 Matlab 的传值调用的效率问题。
最近写了一些 Matlab 程序,想起以前想过的一个东西,记录一下。
编程 » Matlab, 优化
最近做了些东西,用到了 Matlab 的优化工具箱, optimization toolbox。因为以前没用过这东西,今天把这个工具箱的帮助文档基本上翻了一遍。
最近做一些 优化问题 ,找到了 YALMIP 工具包。在其帮助文件里看到如何使用该工具包 求解 sudoku ,整个思路是将问题转化为整数规划问题。这样的思路以前也想到过,但总觉得整数规划问题的求解会更复杂。但是下面的 Matlab 代码,显示它可以非常简洁,思路见程序的注释(程序运行需要安装 YALMIP 工具包):