首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >为什么(在MATLAB中)这段代码更快?

为什么(在MATLAB中)这段代码更快?
EN

Stack Overflow用户
提问于 2013-07-10 21:47:31
回答 2查看 161关注 0票数 5

我在MATLAB中用两种不同的方式写了一些代码。首先,我使用了两个for循环,乍一看似乎很愚蠢:

代码语言:javascript
代码运行次数:0
运行
复制
Initial = [zeros(10,1) ones(10,1)];

for xpop=1:10
    for nvar=1:10
        Parent(xpop,nvar) = Initial(nvar,1)+(Initial(nvar,2)-Initial(nvar,1))*rand();
    end
end

在第二种方案中,我尝试进行向量化计算(我假设它可以更快):

代码语言:javascript
代码运行次数:0
运行
复制
Parent = repmat(Initial(:,1),1,10) + rand(10,10).*(repmat(Initial(:,2),1,10)-repmat(Initial(:,1),1,10));

代码在三次不同运行中的运行时间如下所示:

代码语言:javascript
代码运行次数:0
运行
复制
Elapsed time is 0.000456 seconds.
Elapsed time is 0.006342 seconds.

Elapsed time is 0.000457 seconds.
Elapsed time is 0.006147 seconds.

Elapsed time is 0.000471 seconds.
Elapsed time is 0.006433 seconds.

为什么第一种方案比第二种方案快?它真的在'.*‘命令中做了两个愚蠢的for循环吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-07-10 22:02:30

您的测试设置太小,无法显示向量化的优势。

代码语言:javascript
代码运行次数:0
运行
复制
Initial = [zeros(10,1) ones(10,1)];
Elapsed time is 0.000078 seconds.
Elapsed time is 0.000995 seconds.

现在来看一个更大的问题:

代码语言:javascript
代码运行次数:0
运行
复制
Initial = [zeros(1000,1) ones(1000,1)];
Elapsed time is 2.797949 seconds.
Elapsed time is 0.049859 seconds.
票数 9
EN

Stack Overflow用户

发布于 2013-07-10 22:08:58

这对你测试这些东西是有好处的。但是,您需要学习如何进行这些测试以获得良好的信息。

首先,花费的时间非常小,所以重复测试总是最好的。第二,使用像timeit这样的工具。它为您做了所有的工作,消除了许多错误来源,尽管它需要将其目标封装为一个函数。

接下来,还有一些小问题。您的测试用例非常小。事实上,代码花费时间的原因有很多。考虑功能开销和启动成本。调用函数需要时间,因为设置和销毁函数工作区会产生开销。此外,一个好的函数将进行错误测试,并提供几个选项。但要实现这一点,它必须检查是否设置了这些选项。所以时间被浪费了,通常没有做任何有价值的事情,因为你只想以某种简单的形式使用函数。这意味着,当你调用函数来矢量化一个微小的计算时,它实际上可能比你只做内联的未矢量化的形式需要更多的时间。因此,小的测试用例往往具有误导性。(我打算为更大的问题添加一个时间比较,但那时Marc已经在他的答案中这样做了。对于更大的问题,请参阅背心差异。)

您还应该学习使用bsxfun,这是一个设计用来优化您正在测试的表单的某些计算的工具。再说一次,小问题通常不会在速度上有很大的提高。

接下来,还有JIT的问题,即在MATLAB中对一些简单代码进行优化的加速。如果这个(对你来说不可见的)工具能够很好地处理你正在测试的代码,那么它看起来就像是循环更快了。

做一些测试是很好的,所以让我们做一个比较。由于您的示例主要是内联的,因此我将在每个case周围放置一个大循环。这将减少测试错误的一个主要来源。

代码语言:javascript
代码运行次数:0
运行
复制
Ktot = 100;
N = 10;
Initial = [zeros(N,1) ones(N,1)];

tic
for k = 1:Ktot
  for xpop=1:N
    for nvar=1:N
      Parent(xpop,nvar) = Initial(nvar,1)+(Initial(nvar,2)-Initial(nvar,1))*rand();
    end
  end
end
toc

tic
for k = 1:Ktot
  Parent = repmat(Initial(:,1),1,N) + rand(N,N).*(repmat(Initial(:,2),1,N)-repmat(Initial(:,1),1,N));
end
toc

你能改进你的矢量化形式吗?当一个可以工作时,为什么要做两个repmats?

代码语言:javascript
代码运行次数:0
运行
复制
tic
for k = 1:Ktot
  Parent = repmat(Initial(:,1),1,N) + rand(N,N).*repmat(Initial(:,2)-Initial(:,1),1,N);
end
toc

那bsxfun呢?

代码语言:javascript
代码运行次数:0
运行
复制
tic
for k = 1:Ktot
  Parent = bsxfun(@plus,Initial(:,1),bsxfun(@times,rand(N,N),Initial(:,2)-Initial(:,1)));
end
toc

因此,当N= 10,Ktot = 100时,我看到这样的时间:

代码语言:javascript
代码运行次数:0
运行
复制
Elapsed time is 0.003935 seconds.
Elapsed time is 0.012250 seconds.
Elapsed time is 0.008269 seconds.
Elapsed time is 0.004304 seconds.

再说一次,这是一个小问题。如果我们扩大问题会发生什么?尝试N= 100,而不是N= 10。

代码语言:javascript
代码运行次数:0
运行
复制
Elapsed time is 0.131186 seconds.
Elapsed time is 0.031671 seconds.
Elapsed time is 0.027205 seconds.
Elapsed time is 0.019763 seconds.

因此,我们看到了一些更符合逻辑的事情。现在,bsxfun的变体开始显示出一些好处。接下来,上升到N= 1000。

代码语言:javascript
代码运行次数:0
运行
复制
Elapsed time is 12.288608 seconds.
Elapsed time is 3.412531 seconds.
Elapsed time is 2.690691 seconds.
Elapsed time is 1.626599 seconds.

本质上,所有这些代码都做相同的工作,只是其中一些在如何构建问题方面更有效,而另一些则有更多开销。正如我们在更大的问题中所看到的,显式循环是平坦的。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17572332

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档