在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
关于MATLAB的效率问题,很多文章,包括我之前写的一些,主要集中在使用向量化以及相关的问题上。但是,最近我在实验时对代码进行profile的过程中,发现在新版本的MATLAB下,for-loop已经得到了极大优化,而效率的瓶颈更多是在函数调用和索引访问的过程中。 由于MATLAB特有的解释过程,不同方式的函数调用和元素索引,其效率差别巨大。不恰当的使用方式可能在本来不起眼的地方带来严重的开销,甚至可能使你的代码的运行时间增加上千倍(这就不是多买几台服务器或者增加计算节点能解决的了,呵呵)。 下面通过一些简单例子说明问题。(实验选在装有Windows Vista的一台普通的台式机(Core2 Duo 2.33GHz + 4GB Ram)进行,相比于计算集群, 这可能和大部分朋友的环境更相似一些。实验过程是对某一个过程实施多次的整体进行计时,然后得到每次过程的平均时间,以减少计时误差带来的影响。多次实验,在均值附近正负20%的范围内的置信度高于95%。为了避免算上首次运行时花在预编译上的时间,在开始计时前都进行充分的“热身”运行。) 函数调用的效率 一个非常简单的例子,把向量中每个元素加1。(当然这个例子根本不需要调函数,但是,用它主要是为了减少函数执行本身的时间,突出函数解析和调用的过程。) 作为baseline,先看看最直接的实现 % input u: u is a 1000000 x 1 vector v = u + 1; 这个过程平均需要0.00105 sec。 而使用长期被要求尽量避免的for-loop n = numel(u); % v = zeros(n, 1) has been pre-allocated. for i = 1 : n v(i) = u(i) + 1; end 所需的平均时间大概是0.00110 sec。从统计意义上说,和vectorization已经没有显著差别。无论是for-loop或者vectorization,每秒平均进行约10亿次“索引-读取-加法-写入”的过程,计算资源应该得到了比较充分的利用。 要是这个过程使用了函数调用呢?MATLAB里面支持很多种函数调用方式,主要的有m-function, function handle, anonymous function, inline, 和feval,而feval的主参数可以是字符串名字,function handle, anonymous function或者inline。 用m-function,就是专门定义一个函数 function y = fm(x) y = x + 1;在调用时 for i = 1 : n v(i) = fm(u(i)); end function handle就是用@来把一个function赋到一个变量上,类似于C/C++的函数指针,或者C#里面的delegate的作用 fh = @fm; for i = 1 : n v(i) = fh(u(i)); end anonymous function是一种便捷的语法来构造简单的函数,类似于LISP, Python的lambda表达式 fa = @(x) x + 1; for i = 1 : n v(i) = fa(u(i)); end inline function是一种传统的通过表达式字符串构造函数的过程 fi = inline('x + 1', 'x'); for i = 1 : n v(i) = fi(u(i)); end feval的好处在于可以以字符串方式指定名字来调用函数,当然它也可以接受别的参数。 v(i) = feval('fm', u(i)); v(i) = feval(fh, u(i)); v(i) = feval(fa, u(i));对于100万次调用(包含for-loop本身的开销,函数解析(resolution),压栈,执行加法,退栈,把返回值赋给接收变量),不同的方式的时间差别很大: m-function function handle anonymous function inline function feval('fm', u(i)) feval(fh, u(i)) feval(fa, u(i)) feval(@fm, u(i)) feval(@fa, u(i)) 从这里面,我们可以看到几个有意思的现象:
在2007年以后,MATLAB推出了arrayfun函数,上面的for-loop可以写为 v = arrayfun(fh, u)这平均需要4.48 sec,这比起for-loop(需时0.615 sec)还慢了7倍多。这个看上去“消除了for-loop"的函数,由于其内部设计的原因,未必能带来效率上的正效果。 元素和域的访问 除了函数调用,数据的访问方式对于效率也有很大影响。MATLAB主要支持下面一些形式的访问:
这里主要探索单个元素或者域的访问(当然,MATLAB也支持对于子数组的非常灵活整体索引)。 对于一百万次访问的平均时间 A(i) for a numeric array C{i} for a cell array struct field struct field (with dynamic name) 我们可以看到MATLAB对于单个数组元素或者静态的struct field的访问,可以达到不错的速度,在主流台式机约每秒2亿次(连同for-loop的开销)。而cell array的访问则明显缓慢,约每秒400万次(慢了50倍)。MATLAB还支持灵活的使用字符串来指定要访问域的语法(动态名字),但是,是以巨大的开销为代价的,比起静态的访问慢了200倍以上。 关于Object-oriented Programming MATLAB在新的版本中(尤其是2008版),对于面向对象的编程提供了强大的支持。在2008a中,它对于OO的支持已经不亚于python等的高级脚本语言。但是,我在实验中看到,虽然在语法上提供了全面的支持,但是matlab里面面向对象的效率很低,开销巨大。这里仅举几个例子。
建议 根据上面的分析,对于撰写高效MATLAB代码,我有下面一些建议:
这是一篇转载的文章:http://dahua.spaces.live.com/blog/cns!28AF4251DF30CA42!2459.entry#trackback |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论