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的接口调用,往往会带来非常巨大的开销(成千上万倍的减慢),甚至使得本来几乎不是问题的代码成为性能瓶颈。

doubanclaim43f1a75046c8210a

Q.E.D., ©zhiqiang, 2010.09.24。请参考右边的相关文章列表。


  1. 原来大家都关注大牛Dahua的Blog。。还有大牛您老的Blog~~~ ;-)

  2. zeros 预分配内存空间也能大幅提高效率,不过这个已经是常识了

  3. 为啥你回复我的时候系统没发邮件?
    字符串型的么,就用 cell 初始化吧

  4. 我后来知道一个权宜方法,也不够完美:

    a = blanks(1000000);
    a = reshape(a, 1000, 1000);
    

    你说的用cell初始化是怎么弄?

    btw,这里不是每条回复都会发送邮件,只有我觉得有必要的时候(比如留言者问了个问题),我在回复时会勾选上邮件通知。

  • 支持使用微薄、人人网和QQ的账户登陆进行评论。由各自网站直接认证,不会泄露你的密码。
  • 登陆后可选择分享评论到所绑定的社交网络,如微薄、人人和QQ空间。
  • 评论提交后无法修改。如需修改,请删除原评论再重新提交。
  • 评论支持LaTeX代码,行内公式请用\(a+b=c\),行间公式请用\[a+b=c\]。公式只支持英文字符。
Loading...
Loading...
Loading...