我在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.
为什么第一个方案比第二个方案更快?它真的在'。*'命令中为循环做了两个愚蠢的事吗?
答案 0 :(得分: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.
答案 1 :(得分: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.
基本上,所有这些代码都做同样的工作,只是有些代码在构造问题时效率更高,而有些则有更多的开销。正如我们在较大的问题中所看到的那样,显式循环不变。