一段时间以来,我一直在编写 MATLAB 脚本,但我仍然不明白它是如何“在幕后”工作的。考虑以下脚本,它以三种不同的方式使用(大)向量进行一些计算:
- MATLAB向量运算;
- 简单的循环,按组件进行相同的计算;
- 一个应该比 2. 更快的优化循环,因为避免了一些分配和一些分配。
这是代码:
N = 10000000;
A = linspace(0,100,N);
B = linspace(-100,100,N);
C = linspace(0,200,N);
D = linspace(100,200,N);
% 1. MATLAB Operations
tic
C_ = C./A;
D_ = D./B;
G_ = (A+B)/2;
H_ = (C_+D_)/2;
I_ = (C_.^2+D_.^2)/2;
X = G_ .* H_;
Y = G_ .* H_.^2 + I_;
toc
tic
X;
Y;
toc
% 2. Simple cycle
tic
C_ = zeros(1,N);
D_ = zeros(1,N);
G_ = zeros(1,N);
H_ = zeros(1,N);
I_ = zeros(1,N);
X = zeros(1,N);
Y = zeros(1,N);
for i = 1:N,
C_(i) = C(i)/A(i);
D_(i) = D(i)/B(i);
G_(i) = (A(i)+B(i))/2;
H_(i) = (C_(i)+D_(i))/2;
I_(i) = (C_(i)^2+D_(i)^2)/2;
X(i) = G_(i) * H_(i);
Y(i) = G_(i) * H_(i)^2 + I_(i);
end
toc
tic
X;
Y;
toc
% 3. Opzimized cycle
tic
X = zeros(1,N);
Y = zeros(1,N);
for i = 1:N,
X(i) = (A(i)+B(i))/2 * (( C(i)/A(i) + D(i)/B(i) ) /2);
Y(i) = (A(i)+B(i))/2 * (( C(i)/A(i) + D(i)/B(i) ) /2)^2 + ( (C(i)/A(i))^2 + (D(i)/B(i))^2 ) / 2;
end
toc
tic
X;
Y;
toc
我知道人们总是会尝试向量化计算,因为 MATLAB 在矩阵/向量上构建(因此,如今,它并不总是最好的选择),所以我期待类似的东西:
C = A .* B;
比:
for i in 1:N,
C(i) = A(i) * B(i);
end
我没想到的是,即使在上面的脚本中,它实际上也更快,尽管我使用的第二种和第三种方法只经过一个周期,而第一种方法执行许多向量操作(理论上,这是一个“for " 每次循环)。这迫使我得出结论,MATLAB 有一些魔力可以(例如):
C = A .* B;
D = C .* C;
比单个“for”循环运行得更快,其中包含一些操作。
所以:
- 避免第一部分执行得如此之快的魔法是什么?
- 当您编写“D= A .* B”时,MATLAB 是否实际上使用“for”循环进行组件计算,或者只是跟踪 D 包含“bla”和“bla”的一些乘法?
编辑
- 假设我想使用 C++ 实现相同的计算(可能使用一些库)。MATLAB 的第一种方法会比 C++ 中实现的第三种方法更快吗?(我会自己回答这个问题,请给我一些时间。)
编辑 2
根据要求,这里有实验运行时:
第 1 部分:0.237143
第 2 部分:4.440132 其中 0.195154 用于分配
第 3 部分:2.280640 其中 0.057500 用于分配
并且没有 JIT:
第 1 部分:0.337259
第 2 部分:149.602017 其中 0.033886 用于分配
第 3 部分:82.167713 其中 0.010852 用于分配