怎么用matlab测效率,【转载】MATLAB 效率分析
SimWe个人空间Q;cl\xQ%Li{
于MATLAB的效率问题,很多文章,包括我之前写的一些,主要集中在使用向量化以及相关的问题上。但是,最近我在实验时对代码进行profile的过程中,发现在新版本的MATLAB下,for-loop已经得到了极大优化,而效率的瓶颈更多是在函数调用和索引访问的过程中。
+t0MW~:yK0Lo I's ^0SimWe个人空间 ?| q7z2p
由于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_r('fm', u(i));
v(i) = feval_r(fh, u(i));
v(i) = feval_r(fa, u(i));
对于100万次调用(包含for-loop本身的开销,函数解析(resolution),压栈,执行加法,退栈,把返回值赋给接收变量),不同的方式的时间差别很大:
m-function
0.385 sec
function handle
0.615 sec
anonymous function
0.635 sec
inline function
166.00 sec
feval_r('fm', u(i))
8.328 sec
feval_r(fh, u(i))
0.618 sec
feval_r(fa, u(i))
0.652 sec
feval_r(@fm, u(i))
2.788 sec
feval_r(@fa, u(i))
34.689 sec
从这里面,我们可以看到几个有意思的现象:
首先,调用自定义函数的开销远远高于一个简单的计算过程。直接写 u(i) = v(i) + 1
只需要 0.0011 秒左右,而写u(i) = fm(v(i))
则需要0.385秒,时间多了几百倍,而它们干的是同一件事情。这说明了,函数调用的开销远远大于for-loop自己的开销和简单计算过程——在不同情况可能有点差别,一般而言,一次普通的函数调用花费的时间相当于进行了几百次到一两千次双精度浮点加法。
使用function handle和anonymous
function的开销比使用普通的m-函数要高一些。这可能是由于涉及到句柄解析的时间,而普通的函数在第一次运行前已经在内存中进行预编译。
inline
function的运行时间则令人难以接受了,竟然需要一百多秒(是普通函数调用的四百多倍,是直接计算的十几万倍)。这是因为matlab是在每次运行时临时对字符串表达式(或者它的某种不太高效的内部表达)进行parse。
feval_r(fh, u(i))和fh(u(i)),feval_r(fa,
u(i))和fa(u(i))的运行时间很接近,表明feval在接受句柄为主参数时本身开销很小。但是,surprising的是
for i = 1 : n
v(i) = feval_r(@fm, u(i));
end
比起
fh = @fm;
for i = 1 : n
v(i) = feval_r(fh, u(i));
end
慢了4.5倍 (前者0.618秒,后者2.788秒)。
for i = 1 : n
v(i) = feval_r(@(x) x + 1, u(i));
end
比起
fa = @(x) x + 1;
for i = 1 : n
v(i) = feval_r(fa, u(i));
end
竟然慢了53倍(前者0.652秒,后者34.689秒)。
由于在MATLAB的内部实现中,function
handle的解析是在赋值过程中进行的,所以预先用一个变量把句柄接下,其效果就是预先完成了句柄解析,而如果直接把@fm或者@(x) x
+
1写在参数列上,虽然写法简洁一些,但是解析过程是把参数被赋值到所调函数的局部变量时才进行,每调用一次解析一次,造成了巨大的开销。
feval使用字符串作为函数名字时,所耗时间比传入句柄大,因为这涉及到对函数进行搜索的时间(当然这个搜索是在一个索引好的cache里面进行(除了第一次),而不是在所有path指定的路径中搜索。)
在2007年以后,MATLAB推出了arrayfun函数,上面的for-loop可以写为
v = arrayfun(fh, u)
这平均需要4.48 sec,这比起for-loop(需时0.615
sec)还慢了7倍多。这个看上去“消除了for-loop"的函数,由于其内部设计的原因,未必能带来效率上的正效果。
元素和域的访问
除了函数调用,数据的访问方式对于效率也有很大影响。MATLAB主要支持下面一些形式的访问:
array-index A(i):
cell-index: C{i};
struct field: S.fieldname
struct field (dynamic): S.('fieldname')
这里主要探索单个元素或者域的访问(当然,MATLAB也支持对于子数组的非常灵活整体索引)。
对于一百万次访问的平均时间
A(i) for a numeric array
0.0052 sec
C{i} for a cell array
0.2568 sec
struct field
0.0045 sec
struct field (with dynamic name)
1.0394 sec
我们可以看到MATLAB对于单个数组元素或者静态的struct
field的访问,可以达到不错的速度,在主流台式机约每秒2亿次(连同for-loop的开销)。而cell
array的访问则明显缓慢,约每秒400万次(慢了50倍)。MATLAB还支持灵活的使用字符串来指定要访问域的语法(动态名字),但是,是以巨大的开销为代价的,比起静态的访问慢了200倍以上。
关于Object-oriented
Programming
MATLAB在新的版本中(尤其是2008版),对于面向对象的编程提供了强大的支持。在2008a中,它对于OO的支持已经不亚于python等的高级脚本语言。但是,我在实验中看到,虽然在语法上提供了全面的支持,但是matlab里面面向对象的效率很低,开销巨大。这里仅举几个例子。
object中的property的访问速度是3500万次,比struct
field慢了6-8倍。MATLAB提供了一种叫做dependent
property的属性,非常灵活,但是,效率更低,平均每秒访问速度竟然低至2.6万次(这种速度基本甚至难以用于中小规模的应用中)。
object中method调用的效率也明显低于普通函数的调用,对于instance
method,每百万次调用,平均需时5.8秒,而对于static
method,每百万次调用需时25.8秒。这相当于每次调用都需要临时解析的速度,而matlab的类方法解析的效率目前还明显偏低。
MATLAB中可以通过改写subsref和subsasgn的方法,对于对象的索引和域的访问进行非常灵活的改造,可以通过它创建类似于数组的对象。但是,一个符合要求的subsref的行为比较复杂。在一个提供了subsref的对象中,大部分行为都需要subsref进行调度,而默认的较优的调度方式将被掩盖。在一个提供了subsref的类中(使用一种最快捷的实现),object
property的平均访问速度竟然降到1万5千次每秒。
建议
根据上面的分析,对于撰写高效MATLAB代码,我有下面一些建议:
虽然for-loop的速度有了很大改善,vectorization(向量化)仍旧是改善效率的重要途径,尤其是在能把运算改写成矩阵乘法的情况下,改善尤为显著。
在不少情况下,for-loop本身已经不构成太大问题,尤其是当循环体本身需要较多的计算的时候。这个时候,改善概率的关键在于改善循环体本身而不是去掉for-loop。
MATLAB的函数调用过程(非built-in
function)有显著开销,因此,在效率要求较高的代码中,应该尽可能采用扁平的调用结构,也就是在保持代码清晰和可维护的情况下,尽量直接写表达式和利用built-in
function,避免不必要的自定义函数调用过程。在次数很多的循环体内(包括在cellfun,
arrayfun等实际上蕴含循环的函数)形成长调用链,会带来很大的开销。
在调用函数时,首选built-in function,然后是普通的m-file函数,然后才是function
handle或者anonymous function。在使用function handle或者anonymous
function作为参数传递时,如果该函数被调用多次,最好先用一个变量接住,再传入该变量。这样,可以有效避免重复的解析过程。
在可能的情况下,使用numeric array或者struct array,它们的效率大幅度高于cell
array(几十倍甚至更多)。对于struct,尽可能使用普通的域(字段,field)访问方式,在非效率关键,执行次数较少,而灵活性要求较高的代码中,可以考虑使用动态名称的域访问。
虽然object-oriented从软件工程的角度更为优胜,而且object的使用很多时候很方便,但是MATLAB目前对于OO的实现效率很低,在效率关键的代码中应该慎用objects。
如果需要设计类,应该尽可能采用普通的property,而避免灵活但是效率很低的dependent
property。如非确实必要,避免重载subsref和subsasgn函数,因为这会全面接管对于object的接口调用,往往会带来非常巨大的开销(成千上万倍的减慢),甚至使得本来几乎不是问题的代码成为性能瓶颈。
怎么用matlab测效率,【转载】MATLAB 效率分析相关推荐
- lyapunov函数 matlab,科学网-[转载]Matlab的Lyapunov、Sylvester和Riccati方程的Matlab求解-吴雄君的博文...
一.连续Lyapunov方程连续Lyapunov方程可以表示为 Lyapunov方程来源与微分方程稳定性理论,其中要求C为对称正定的n×n方阵,从而可以证明解X亦为n×n对称矩阵,这类方程直接求解比较 ...
- matlab灰度归一化,[转载]matlab图像处理为什么要归一化和如何归一化?
matlab图像处理为什么要归一化和如何归一化?一.为什么归一化 1. 基本上归一化思想是利用图像的不变矩寻找一组参数使其能够消除其他变换函数对图像变换的影响.也就是转换成唯一的标准形式以抵抗仿射变换 ...
- matlab 比较日期,[转载][Matlab]关于时间的函数的不完全总结
一.日期和时间 三种表示格式 (1)日期字符串: NowDate=date NowDate = 08-Aug-2008 year(NowDate) %提取日期的年份 ans = 2008 (2)连续的 ...
- MATLAB阶段性方程组,[转载]matlab 解方程组
1.解方程 最近有多人问如何用matlab解方程组的问题,其实在matlab中解方程组还是很方便的,例如,对于代数方程组Ax=b(A为系数矩阵,非奇异)的求解,MATLAB中有两种方法: (1)x=i ...
- matlab水汽计算公式,[转载]matlab 解方程组
1.解方程 最近有多人问如何用matlab解方程组的问题,其实在matlab中解方程组还是很方便的,例如,对于代数方程组Ax=b(A为系数矩阵,非奇异)的求解,MATLAB中有两种方法: (1)x=i ...
- matlab画图模糊,[转载]matlab中模糊工具箱的使用
用 Matlab 中的 Fuzzy 工具箱做一个简单的模糊控制,流程如下: 1.创建一个 FIS (Fuzzy Inference System ) 对象, a = newfis(fisName,fi ...
- matlab msgbox 换行,[转载]Matlab/GUI笔记
转自 http://www.kylen314.com/archives/412 不显示坐标刻度: set(gca,'xtick',[]) =============================== ...
- matlab中select,[转载]MATLAB阈值获取函数ddencmp、thselect、wbmpen和w
crit(t) wdcbm的调用格式有以下两种: (1)[THR,NKEEP]=wdcbm(C,L,ALPHA); (2)[THR,NKEEP]=wdcbm(C,L,ALPHA,M); 函数wdcbm ...
- matlab二进制操作,[转载]Matlab二进制类型数据相关操作
Matlab逻辑运算 &: 逻辑与 A&B 返回值:当A.B中元素均为非零元素是,返回1,否则,为0,还可表示成and(a,b); | :逻辑或 A|B.还可以表示成or(A,B). ...
- matlab xcorr lags,[转载]matlab中xcorr的用法
Matlab中用于计算自相关函数的指令是xcorr.比如矩阵A=[1 2 3]; xcorr(A)=3.0000 8.0000 14.0000 8.0000 3.0000 自相关函数是信号间隔的函数, ...
最新文章
- AI领域3种典型的深度学习算法
- 《战狼2》中人脸识别无人机表现不俗,军、警用果真如此高能?
- code first基础
- IPhone 设备状态、闪光灯状态
- spring boot发送普通文本邮件/HTML邮件/附件邮件/图片邮件完整代码
- 什么是计算机计算机网络的主要功能是什么,计算机网络的三大主要功能是什么?-与非网...
- 2021李宏毅机器学习课程笔记——Adversarial Attack
- hibernate 并发获取session失败 空指针_高并发之|通过ThreadPoolExecutor类的源码深度解析线程池执行任务的核心流程...
- cfb为什么不需要填充_高压输电线路中为什么只有相线不需要零线
- BZOJ4570: [Scoi2016]妖怪
- c++11 实现半同步半异步线程池
- 《深度学习Python实践》附录——聚类分析
- 『开发』小程序通过易班接口登陆并请求数据
- WIN10便签怎么样开启免打扰时间 如何设置休息日消息免打扰
- Amit Chadha出任LT Technology Services 首席执行官兼董事总经理
- 基于Pygame中Pygame模块的大战外星人
- epub格式电子书剖析 .
- C++学习(第二篇)//最详细C++运算符经验
- vue.js动态设置VueComponent高度遇到的问题
- 西游记中孙悟空有哪些技能?
热门文章
- Win10系统电脑开机黑屏一直转圈无法进入桌面怎么办?
- Telnet是什么协议?如何使用?
- docker(6):仓库
- 视频转换gif图是怎么做的?怎么把视频转成gif表情包?
- IDEA循环MAP的快捷键和自己常用的循环MAP方式
- 质监局监督检测指挥系统
- 我的世界服务器插件制作器,我的世界插件制作详细图文教程 教你制作强大的CraftBukkit插件[转载]...
- atmega8 例程:12864例程
- 农村土地确权之例会纪要—— 新蔡县土地确权第十次例会及苍穹软件第二次培训纪要
- 第五次作业:项目选题