MATLAB高效编程

1 向量化编程 vs. 循环加速

        从我们使用MATLAB的第一天开始,就有人告诉我们,MATLAB的优势是矩阵运算,所以能不写循环就不要写循环,否则速度会很慢。

        但是,从6.5版本开始(貌似我们大一就是用的6.5,六七年前的事情了),MATLAB就引入了针对循环加速的JIT(Just In Time)技术和accelerator,默认情况下,这2个加速器都是打开的,可以通过如下命令关闭或者打开:

feature jit on

feature jit off

feature accel on

feature accel off

可以观察到打开或者关闭它们前后的性能差异是很大的。

       从r2009a之后,循环本身已经不再是瓶颈了,瓶颈更多来源于循环体内部的代码执行方式。所以没必要千方百计地把循环改成向量化编程,有时这种改动不仅不能带来性能的提升,还会使得代码的运行时间变长。

       举个简单的例子,sliding window在很多地方都会用到,比如MAC层中的访问控制、物理层中的同步。从算法上讲,sliding window是可以由上次迭代的值和本次的值进行简化运算的。当sliding window的长度很小的时候,我试过长度为256时向量化编程可以使运行速度更快,但毫无疑问,当长度增长到某一个值之后,采用循环的迭代肯定会更快。至于这个临界值是多少,有兴趣的可以自己去试试~

2 调用系统函数 vs. 自己手动写

       我曾经试过,自己写了个16QAM的硬判决函数,结果运行时间是系统自带函数的3倍~。

       但是,也有另外一种观点,说MATLAB的系统函数由于要实现很强的通用性,函数内部都会有很多的if-else类似的条件分支,会拖慢速度,这个就需要具体问题具体分析了。按我的理解,如果函数复杂,则就算系统函数的if-else很多,尽量还是调用,如果函数简单,比如有些函数真正的算法只有一行,那完全可以自己写,避免了if-else带来的时间损耗。当然,如果要做到极致,可以把系统函数拷到自己的工程下面,按照自己调用的需求把函数内部多余的分支去掉,但我想一般人也不会这么去做,麻烦。

3 预分配内存

       这种技巧在C/C++中是不存在的,因为C/C++的语法就要求使用数组要预先定义,但偏偏MATLAB不一样,我们可以不定义变量就使用,这种规则在方便我们编程的同时也带来一些容易被我们忽视的问题,其中,预分配内存是受到严重影响的一个方面。该问题一般发生在包含循环的代码中,比如:

for k = 1 : 100

    a(k) = k;

end

       编写人员的本意是生成一个递增的数列,长度100。如果没有在for循环前面加上一行a = zeros(100, 1)的话,代码检测器会报waring,说数组a在每次循环的时候会改变大小,会拖慢速度。究其原因,是因为如果不预分配的话,MATLAB在每次循环执行的时候都会改变数组a的大小,则每次都会去内存查找适合此大小的内存区间,然后重新分配内存。该过程不仅会造成MATLAB代码的执行速度变慢,而且很容易造成内存碎片化。

       了解了预分配内存的好处,那我们应该怎么初始化内存呢?前面已经提到一种方法:

a = zeros(100, 1);

       还可以在zeros后续指定数据类型(一般情况下我们用的double),指定数据类型的好处不仅仅是提前知道整个数组需要的内存空间,而且在MATLAB中还有个特殊的优势:如果指定数据类型,则在初始化的时候MATLAB只会分配这么一块内存出来,并不会全部写入0,这样就节约了时间;而不指定的话,在分配空间之后还会把所有的内存空间初始化为0。所以,如果我们保证该数组的每个元素在循环体内部能够被一一赋值,为了加快速度,指明数据类型是必要的。

ps:关于这一点,和我准备下一篇博文准备要讲的MATLAB并行编程是不同的,千万注意!

4 少用cell

       cell的数组不需要连续的内存空间进行存储,所以需要记录每个cell单元的地址等信息,即使是空的cell单元,也会占用4个字节的存储空间。而且cell的访问是很慢的。

5 列优先准则

       MATLAB和C/C++是不同的,C/C++中的二维数组存储到内存中,是按行优先准则存储的,而MATLAB中是列优先准则。这种差异导致我们在进行MATLAB编程的时候要特别注意,向量化编程的时候尽量取一个二维数组的列,而不是行。

       同理,在循环中也尽量如此。

6 循环注意事项

       不管是在MATLAB还是C/C++中,都有一个准则叫:变勤拿少取为少拿多取。具体含义就是减小循环的次数,尽量在每次循环中做比较多的数据处理,不管是数据读取还是写入。因此,在编写带有嵌套循环的代码时,循环次数多的尽量在内层,少的尽量在外层。

7 数值索引 vs. 逻辑索引

       来看看下面2种方式的索引:

a(a>0.5) = 0; % 逻辑索引

a(find(a>0.5)) = 0; % 数值索引

毫无疑问,前者的代码执行速度更快。

       关于这一点,我问过一些高手为什么会有这种情况,但他们也没能给出个令人信服的解释。目前比较说得过去的解释是:在用数值索引时调用了find函数,当然会有额外时间的增加。仔细想想,貌似也说得过去。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值