wanglu 发表于 2010-10-7 07:38:55

关于matlab代码矢量化的理解

代码矢量化是matlab的精髓,其基本特点是运行速度快和代码简洁,它是如何实现的?

按我的理解,代码矢量化的本质就是设计专门的函数对数组元素集中运算,这样可提高运行速度,同时兼有代码简洁的特点。

对matlab的理解比较肤浅,但也确实看不出有更深意义的东西,望解惑。

大家有什么看法,愿畅所欲言。

qibbxxt 发表于 2010-10-7 12:39:51

本帖最后由 qibbxxt 于 2010-10-7 13:20 编辑

矢量化使代码简洁,高效,但是不容易读懂,所以要多写注释

wanglu 发表于 2010-10-7 15:59:10

矢量化使代码简洁,高效,但是不容易读懂,所以要多写注释
qibbxxt 发表于 2010-10-7 12:39 http://forum.simwe.com/images/common/back.gif
恩,矢量化是靠函数实现的,函数用法比较复杂,需要看说明,当函数量多时,代码确实不太容易懂。

而非矢量化代码即常说的循环、分支、顺序结构,一般人看起来比较上眼。但如果是脚本运行,效率较低。

我特别想知道matlab的矢量化是如何实现的?

我正在练手设计的FcMath库也打算以矩阵运算为基础,设计一些专门的函数对数组元素集中运算,运行效率确实有所提高(甚至有些涉及矩阵的算法比matlab还快),代码也简洁了,但不知这是不是矢量化?

脚本运行效率应该取决于函数调度效率、对象管理效率和函数内部算法的实现。

我感觉,matlab的函数调度效率较低,对象管理效率这个不好说,但一些函数内部的设计比较优秀。故有些Forcal代码比matlab快,而有些慢。

详细说明:Forcal数值计算扩展动态库FcMath
源代码下载:http://www.forcal.net/xiazai/forcal9/forcal9code.rar
一些例子:http://www.madio.net/thread-102469-1-1.html
或者这里:http://www.matlabsky.net/thread-9711-1-1.html

messenger 发表于 2010-10-7 18:06:10

矢量化是没有办法的办法,如果循环效率不低,不要学。

wanglu 发表于 2010-10-7 19:03:08

矢量化是没有办法的办法,如果循环效率不低,不要学。
messenger 发表于 2010-10-7 18:06 http://forum.simwe.com/images/common/back.gif
所谓矢量化,就是用函数对大批数据整体操作,其效率要比通过循环一个一个取出来运算效率要高,尽管循环效率可能不低,但脚本取数组元素的值是比较耗时的,故感觉矢量化函数还是有存在的必要。

不知为什么叫代码矢量化,感觉叫“代码函数化”似乎更合适些?

matlab高版本新增的函数arrayfun受到大家的喜爱就是一例。在Forcal中很容易增加这个函数,比设计Sum函数或ndgrid函数要简单地多。

feynmand 发表于 2010-10-7 21:45:18

你说的这个代码函数化其实本质上还是叫做矢量化更合适,因为将代码写作函数本身并没有提高效率。提高效率的是矢量化。matlab内嵌的源代码处理矢量数据效率很高,矢量化只是利用了这一特点。

wanglu 发表于 2010-10-8 07:55:40

你说的这个代码函数化其实本质上还是叫做矢量化更合适,因为将代码写作函数本身并没有提高效率。提高效率的是矢量化。matlab内嵌的源代码处理矢量数据效率很高,矢量化只是利用了这一特点。
feynmand 发表于 2010-10-7 21:45 http://forum.simwe.com/images/common/back.gif
“将代码写作函数”,不是用脚本写函数,而是用C/C++/Fortran等写函数,自然效率高,如matlab的sum、arrayfun等函数,同时矩阵乘a*b只是运算符重载,仍然使用函数实现。

矢量化只是设计函数时提高效率的一种手段,还有很多方法可以提高函数的效率。况且matlab的很多所谓的矢量化函数并不使用矢量化手段,例如ndgrid函数用来生成一些矩阵,reshape函数则干脆只对数组维数进行修改。

代码函数化的意思是将常用的一些操作封装到函数中,设计函数时使用各种手段提高效率,包括矢量化,而函数接口尽可能简单易用,故感觉叫代码函数化更合适?

矢量化操作是不是这样(C语言描述,以sin函数为例):

设a、b为数组指针,n为数组大小:
for(i=0;i<n;i++) a=sin(b);
或者:
for(i=0;i<n;i++) *(a+i)=sin(*(b+i));
或者,aa也是一个数组指针:
aa=a+n;
while(a<aa){*(a++)=sin(*(b++))};
没有测试哪种效率更高?我目前用第一种,似乎效率是最低的。
或许还有其他更好的算法。

在设计函数时,函数内的多重循环尽可能用单纯循环代替,例如下面帖子的例子(该例子是仿照matlab例子做的)。

wanglu 发表于 2010-10-8 08:01:05

本帖最后由 wanglu 于 2010-10-8 08:04 编辑

在matlab中,纯for循环速度最慢。而一半for循环+一半向量化的速度最快,Forcal中也是如此: !using["math","sys"];
mvar:
(:p1,p2,p3,a,b)=
{
oo{
    a=array.rand(),
    b=array.rand(),
    p1=array,
    p2=array,
    p3=array,
    t0=clock(),
    ndgrid(a,b,&A,&B),//完全矢量化
    p1.=A+B
},
printff{"\r\nndgrid: {1,r}",/1000},
lena=FCDLen(a),
lenb=FCDLen(b),
t0=clock(),
m = lenb, (m--, m>=0).while{
    oo{p2(m,neg) = a+rn}//部分矢量化
},
printff{"\r\nfor1: {1,r}",/1000},
t0=clock(),
m = lenb, (m--, m>=0).while{
    n = lena, (n--, n>=0).while{
      //p3(m,n) = a(n)+b(m),//用这句还要慢一些
      A(p3,m,n) = A(a,n)+A(b,m)//不用矢量化
    }
},
printff{"\r\nfor2: {1,r}",/1000}
};
运行5次,结果如下:
ndgrid: 6.2e-002
for1: 1.6e-002
for2: 1.875

ndgrid: 3.1e-002
for1: 1.6e-002
for2: 1.875

ndgrid: 3.1e-002
for1: 3.1e-002
for2: 1.875

ndgrid: 3.2000000000000001e-002
for1: 3.1e-002
for2: 1.859

ndgrid: 4.7e-002
for1: 1.4999999999999999e-002
for2: 1.875

wanglu 发表于 2010-10-8 16:42:33

脚本运行效率应该取决于函数调度效率、对象管理效率和函数内部算法的实现。

以下例子体现了Forcal和matlab的效率差别所在。

例子1:

这个程序段是网友lin2009 给出的,理论结果是每个元素均为275000。
clear all
clc
tic
k = zeros(5,5); % //生成5×5全0矩阵
% 循环计算以下程序段100000次:
for m = 1:100000
    a = rand(5,7);
    b = rand(7,5);%//生成5×7矩阵a,7×5矩阵b,用0~1之间的随机数初始化
    k = k + a * b + a(1:5, 2:6) * b(2:6, 1:5) - a(:, 7) * b(3, :);
end
k
toc

Forcal代码1:运行稍快的代码,比matlab约快10%吧?
!using["math","sys"];
mvar:
t0=clock(),
oo{k=zeros},
i=0,((i++)<100000).while{
oo{
    a=rand, b=rand,
    k.oset
}
},
k.outm(),
/1000;
在我的电脑上运行时间为3.344秒。

Forcal代码2:比较好看些的代码,似乎也比matlab稍快吧?
!using["math","sys"];
(:t0,k,i,a,b)=
{
t0=clock(),
oo{k=zeros},
i=0,((i++)<100000).while{
    oo{
      a=rand, b=rand,
      k.=k+a*b+a(0,4:1,5)*b(1,5:0,4)-a(neg:6)*b(3:neg)
    }
},
k.outm(),
/1000
};
在我的电脑上运行时间为3.579秒。

例子2:

一段程序的Forcal实现:

//用C++代码描述为:
s=0.0;
for(x=0.0;x<=1.0;x=x+0.0011)
{
   for(y=1.0;y<=2.0;y=y+0.0009)
   {
   s=s+cos(1-sin(1.2*x^(y/2)+cos(1-sin(1.2*y^(x/2)))));
   }
}

这个程序段是网友yycs001给出的。
%file speedtest.m
function speedtest
format long
tic
=meshgrid(0:0.0011:1,1:0.0009:2);
s=sum(sum(cos(1-sin(1.2*x.^(y/2)+cos(1-sin(1.2*y.^(x/2)))))))
toc

Forcal代码1:多维数组求和函数Sum,完全矢量化的代码
!using["math","sys"];
mvar:
t=clock(),
oo{
ndgrid,
Sum
};
/1000;
结果:
1008606.64947441
0.625   //时间

或者这个,与上面效率差别不大:
!using["math","sys"];
mvar:
t=clock(),
oo{
ndgrid,
Sum]
};
/1000;

Forcal代码2:求和函数sum,非矢量化代码
f(x,y)=cos(1-sin(1.2*x^(y/2)+cos(1-sin(1.2*y^(x/2)))));
sum["f",0,1,0.0011,1,2,0.0009];
结果:
1008606.64947441
0.719   //时间

Forcal代码3:while循环
mvar:
t=sys::clock();
s=0,x=0,
while{x<=1,//while循环算法;
   y=1,
   while{y<=2,
       s=s+cos(1-sin(1.2*x^(y/2)+cos(1-sin(1.2*y^(x/2))))),
       y=y+0.0009
      },
   x=x+0.0011
},
s;
/1000;
结果:
1008606.64947441
0.734   //时间

大家可下载OpenFC进行测试:http://www.forcal.net/xiazai/forcal9/openfc32w.rar

注意Forcal的矢量化代码第一次运行有时效率较低。

例子1中Forcal和matlab都是矢量化代码,但matlab跑不过Forcal。该例子的特点是函数调用频繁,临时变量生成多,但矩阵很小,矩阵的各种函数运行时耗时较少。故说明Forcal函数调用+变量管理效率优于matlab。

例子2中Forcal的矢量化代码是最快的,但与matlab的矢量化代码相比仍有差距。该例子的特点是函数调用少,临时变量也少,但矩阵大。故说明Forcal的各种矩阵函数Sin、Cos及矩阵的加减运算等函数的内部设计不及matlab。

如能在函数内部设计上下点功夫,例子2超越matlab也是可能的。在这方面,期待国内高手的指点和参与。
页: [1]
查看完整版本: 关于matlab代码矢量化的理解