注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

notes

@-@

 
 
 

日志

 
 
 
 

提高matlab运行速度的心得(转自振动论坛)  

2009-03-02 19:03:12|  分类: matlab |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
 首先说说Matlab与 其他语言的差异:例如对于C或者C++来说,只要算法的思想不变、采用的数据结构相同,不同人写出来的语句在效率上一般不会产生太大的差别。所以,对于C 来说,程序的好坏一般由算法来决定。但是,在matlab中,同样的算法、同样的结构、同样的流程,如果采用的语句不一样,在效率上就会大大不同。所以, 我认为,使用matlab比使用其他语言更加困难,也显得matlab更难以掌握。另外,由于matlab在存储管理上的不便,使得在同时提高时空两域的 效率变得更加困难,特别是在空间上(因为在时间上matlab提供了profiler这个非常有用的工具,但是在空间上就没有)。当需要处理大量的数据 时,精简时空两域的程序语句就尤为重要了。

空间上:

1. 建议使用A = logical(sparse(m,n)),不建议使用 A = sparse(false(m,n)),两者结果一样,但是后者生成m×n的临时矩阵,浪费空间,且当m、n很大时,后者不一定能申请成功;

2. 使用sparse几点注意:

a) 只能用在二维以下的矩阵上;

b) 由于matlab按照“先行后列”的方式读取数据(即先把第一列所有行读取完以后再读取第二列的各行),因此定义稀疏矩阵时,最好“行数>列数”, 这样有利于寻址和空间的节省(自己试试a=sparse(10,5); whos a和b= sparse(5,10);whos b就知道了);

c) 对大型矩阵用sparse非常有效(不但节省空间,而且加快速度,强烈推荐!这在动态申请数组空间的时候尤其方便,当然了,数组不是太大的时候也可以使用 eval即字符串的方法),但对小型矩阵使用反而增加存储量(自己试试a=false(5,1); whos a和b=logical(sparse(5,1));whos b就知道了),相信这是由稀疏矩阵需要存储额外的信息引起的。

3. 尽量按照精度来选择数据的类型,例如,如果只需用到0-255之间的整数,则定义矩阵为uint8型就ok了,定义方式:A = zeros(10,10,’uint8’);可以用intmin(‘uint8’)和intmax(‘uint8’)返回该种类型的最值。


时间上:

1. 在for循环中,清零操作用赋值语句 A = B,其中B是在for循环外的一个同A大小一样的全0阵,不要使用A(:) = 0;但这样会大大影响后面的逐点运算速度。这个问题要请教高手,那就是“个别语句的改动会引起其他语句的执行速度”。例如分别运行3万多次下面代码,但执 行时间有较大差别:
    iLen = length(find(alRegion));   % 0.58 s
    if iLen >= iThreshold_2  % 0.05 s
    …
    end

    iLen = sum(alRegion); % 0.37 s
    if iLen >= iThreshold_2  % 0.40 s
    …
    end
   
2. Find函数较慢,可用logical函数代替,但是,当需要取得满足条件的下标时,就无法使用logical函数,这已经在我之前的帖子中提及过。不 过,大家有没有想过,连find函数都可以进行优化的,方法是用“基本矩阵进行显式逻辑引用”来代替find。例如,假设矩阵A是一个61*73*20的 三维逻辑矩阵,如果用下面的语句循环3万多次,需要的时间是13 s :

    B = find(A == true);

如果采用下面的方法,则只需要不到0.7 s:

首先定义一个索引矩阵:
    a3iCubicIdx = uint32(1:iTotalVoxel); % uint32可以根据需要调整,这里省略了条件判断
    a3iCubicIdx = reshape(a3iCubicIdx, [iVolLen, iVolWdh, iVolHgh]);
然后在循环中写以下代码:
    a3iTemp = a3iCubicIdx(iXmin:iXMax,iYMin:iYMax,iZMin:iZMax);
    B = aiTemp(A(iXmin:iXMax,iYMin:iYMax,iZMin:iZMax));

当然了,改进的前提是知道矩阵A的非零元(即值为true的元素)大致的分布,也就是能够求出iXmin:iXMax,iYMin:iYMax,iZMin:iZMax这个范围。现在终于明白并体会到cwit所说的“连num2str都优化过”的含义了。

3. 不断优化代码,例如corrcoef函数,matlab自带的corrcoef函数求整个矩阵所有列的相关系数,因为我只需要求出某一列跟其他各列的相关 系数,所以参照corrcoef函数自己写了一个,不但把速度提了上去,而且还发现了:repmat(5,100,1)的速度并不比 ones(100,1)*5 快,另外,别小看一个小矩阵的转置操作,当循环次数很大的时候,有没有转置就相差很远了。

4. 使用逻辑运算符&、| 时,两个操作对象最好是logical类型,否则速度会减慢。

5. 二维矩阵转置操作可以用以下三种方法进行,三者的效率基本一样(时空),如果遇到三维以上的矩阵要转置,用permute命令较为方便:
a) A = A’;
b) A = permute(A,[2,1]);
c) A = shiftdim(A,1);

6. “使用eval方式动态存储多个一维数组”比“使用二维数组动态存储多个一维数组”要快,即:eval(['A_', num2str(k),' = B;']);比 A(k,:) = B; 快,其中B是一个一维数组,k表示循环次数。注:并非所有B都进行存储,只存储满足某个条件的B,另外,对预申请空间A不成功,这是对结论的补充说明。值 得注意的是,如果对B是一个稀疏的一维数组,则eval方式的优势荡然无存,当k增大时反而增加系统开销。

7. 当矩阵很大时,利用A(:,k+1:end)=[];去掉多余元素操作时会减慢程序的运行,因此,如果后续处理中没有用到这些多余元素,则没有必要使用这个语句,即不管就是了。

8. 当需要对很大的一个矩阵进行操作时,可以考虑使用循环来完成。例如corrcoef函数,如果处理的对象矩阵A是100*180时(即对100个列向量求 它们两两之间的相关程度,假设需要的只是前面99个与第100个向量的相关系数,其他不需要用到),直接用corrcoef(A)会比较慢,这时候可以考 虑把矩阵A分为5个部分,每个子块与第100个向量进行相关,这样速度会更快。

9. 局部比较、赋值比全局比较、赋值要快(呵呵,这是废话),假设A、B都是三维逻辑矩阵,如果只想对某个局部(例如 X_1:X_2,Y_1:Y_2,Z_1:Z_2这个立方体)进行比较和赋值,则推荐使用B(X_1:X_2,Y_1:Y_2,Z_1:Z_2) = B(X_1:X_2,Y_1:Y_2,Z_1:Z_2) & ~A(X_1:X_2,Y_1:Y_2,Z_1:Z_2),这比B(A) = false或B = B&~A速度上都要快不少。



后记:

1. 此心得原来打算在cwit给了我回复,指正文中错误之后再整理并发布的,不过cwit很忙,所以不知道要等到什么时候,因此先献丑了,有错误大家也帮忙指 正一下。特别是happy、bainhome、jimin,还有Tla等高手。(注:之前写的版本与这个版本略有不同,主要是删除了“循环总次数固定无论 使用多少个for循环速度不变”这句,因为考虑到矩阵维数会影响速度)

2. 上述技巧都是出自我在处理大型矩阵时候自己总结出来的个人心得,转载时请注明。

3. 实验室中由于只有我一个人比较关心matlab的运行速度和存储空间问题(尤其是速度问题),所以不像cwit那样,有一个团队可以互相讨论互相提高,因此,我的心得难免有错误或理解不当的地方,请大家见谅。

4. 错误的地方待cwit给我完整的回复后我会跟贴更正或补充。
  评论这张
 
阅读(1003)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018