5

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

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

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

Parent = repmat(Initial(:,1),1,10) + rand(10,10).*(repmat(Initial(:,2),1,10)-repmat(Initial(:,1),1,10));

三种不同的代码运行所用的时间如下所示:

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.

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

4

2 回答 2

9

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

Initial = [zeros(10,1) ones(10,1)];
Elapsed time is 0.000078 seconds.
Elapsed time is 0.000995 seconds.

现在解决一个更大的问题:

Initial = [zeros(1000,1) ones(1000,1)];
Elapsed time is 2.797949 seconds.
Elapsed time is 0.049859 seconds.
于 2013-07-10T14:02:30.133 回答
3

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

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

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

您还应该学习使用 bsxfun,这是一种旨在优化您正在测试的表单的某些计算的工具。同样,小问题通常不会显示出太多的速度提升(如果有的话)。

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

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

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,一个也可以工作?

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

bsxfun 呢?

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 时,我看到这样的时间:

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。

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。

Elapsed time is 12.288608 seconds.
Elapsed time is 3.412531 seconds.
Elapsed time is 2.690691 seconds.
Elapsed time is 1.626599 seconds.

本质上,所有这些代码都做同样的工作,只是有些代码在构建问题的方式上更有效,而有些代码的开销更大。正如我们在更大的问题中看到的那样,显式循环变得平坦。

于 2013-07-10T14:08:58.057 回答