为什么MATLAB的按元素幂运算可加速512个元素?

时间:2019-07-10 14:00:39

标签: matlab exponentiation

MATLAB的幂函数用于计算常数基和指数数组的元素取指数,当数组的大小变为512时,速度显着提高。我希望看到计算时间随输入大小而增加,但是当指数数组中有512个元素时,下降明显。这是示例代码

...\vendor\laravel\framework\src\Illuminate\Foundation\Auth\RedirectsUsers.php

代码的输出是

x_list = 510:514;
for i = 1:numel(x_list)
    x = x_list(i);
    tic
    for j = 1:10000
        y = power(2,1:x); 
    end
    toc
end

这是怎么回事?

image

3 个答案:

答案 0 :(得分:9)

使用指数随机数的效果相同,就像使用1:n范围内的整数一样:

x = 500:540;
t = zeros(size(x));
for ii = 1:numel(x)
    %m = 1:x(ii);
    m = 500*rand(1,x(ii));
    t(ii) = timeit(@()power(2,m));
end
plot(x,t)

graph showing jump down in execution time around 512

当迫使MATLAB使用maxNumCompThreads(1)的单个线程,并再次运行上面的代码时,我看到的是这张图(注意y轴,峰值只是噪声):

graph not showing the jump at 512

在我看来,MATLAB使用单个核来计算511个值的指数,并且如果矩阵较大,则会激发所有核。使用多线程会产生开销,对于小型数组而言,这样做是不值得的。通过节省时间来平衡开销的确切点取决于许多因素,因此,硬编码固定阈值以决定何时切换到多线程计算,会导致具有与系统特性不同的系统的执行时间增加。确定阈值。

请注意,由于@ norok2在其系统MATLAB was limited to a single thread上,因此没有看到相同的跳转。

答案 1 :(得分:4)

这与计算功率的数字大小有关,而不与容器的大小有关。

如果使用随机数,则对于不同的容器大小,不会在时间上出现跳跃:

x = 450:1550;
y = zeros(numel(x), 1);
X = rand(1, 10000);
for i = 1:length(x)
    f = @() 2 .^ X(1:x(i));
    y(i) = timeit(f);
end

figure()
plot(x, y)

power_test_1

因此,问题必须出在很大数字的计算上。 首先,我认为这可能与溢出有关,但是溢出发生在2 ^ 1024 == inf上,这是MATLAB遵循的IEEE standards所决定的,我认为对于inf比计算一个实数快得多。

以下基准测试支持这一点,其中数组的大小保持不变:

x = 450:1550;
y = zeros(numel(x), 1);
X = rand(1, 10000);
for i = 1:length(x)
    f = @() 2 .^ (ones(1, 500) * x(i));
    y(i) = timeit(f);
end

figure()
plot(x, y)

power_test_2

当我不了解2 ^ 512而不是2 ^ 1024时,为什么这可能与您的设置有关?

(请注意,我使用的是2 .^ ...而不是power(2, ...),但结果是相同的。)


此外,在我的系统中运行@CrisLuengo的代码并不会真正产生任何跳转。

x = 500:540;
t = zeros(size(x));
for ii = 1:numel(x)
    %m = 1:x(ii);
    m = 500*rand(1,x(ii));
    t(ii) = timeit(@()power(2,m));
end
plot(x,t)
到目前为止,所有证据表明峰值与JIT延迟/预热有关。

enter image description here

答案 2 :(得分:4)

以下是使用运行MATLAB R2018a的4核Windows机器对Cris found进行的确认。我首先测试了以下代码,以表明指数的特定值不是跳转的罪魁祸首:

t = zeros(4, 1000);
for p = 1:size(t, 1)
  for n = 1:size(t, 2)
    t(p, n) = timeit(@() power(2, (2.^(p-1)).*ones(1, n)));
  end
end

结果如下:

enter image description here

对于退化边缘情况,指数为1(返回相同的值)或2(返回值乘以自身),计算将按预期运行。但是,与数组大小超过512时指数为4和8的计算时间减少相比,在数组大小为512或更大的情况下跳转表明增加到这些边缘情况。值只是复制上面的曲线。

然后我又进行了两个测试:一个数组大小在1到511之间,第二个数组大小在512到1024之间。这是处理器负载的样子:

enter image description here

处理器3在第一次测试期间显示出很大的负载峰值,而所有4个处理器在第二次测试期间显示出了负载峰值。这证实了多线程用于512或更大的数组大小。这也解释了对于大尺寸边缘情况的计算较慢,因为多线程的开销超过了通过拆分较简单的计算所提供的加速。