在MATLAB中交换两个元素的性能

时间:2009-02-02 05:24:11

标签: performance matlab

纯粹作为一个实验,我在MATLAB中编写排序函数,然后通过MATLAB分析器运行它们。我觉得最令人困惑的方面是交换元素。

我发现在矩阵中交换两个元素的“官方”方式

self.Data([i1, i2]) = self.Data([i2, i1])

比在四行代码中运行要慢得多:

e1 = self.Data(i1);
e2 = self.Data(i2);
self.Data(i1) = e2;
self.Data(i2) = e1;

第二个示例占用的总时间 12次小于第一个示例中的单行代码。

有人会解释为什么吗?

5 个答案:

答案 0 :(得分:6)

根据发布的建议,我进行了一些测试。 当分配的LHS和RHS中引用相同的矩阵时,似乎会出现性能损失。

我的理论是MATLAB使用内部引用计数/写时复制机制,这导致整个矩阵在内部被引用时被内部复制。 (这是猜测,因为我不知道MATLAB的内部结构。)

以下是调用函数885548次的结果。 (这里的差异是第四次,而不是我最初发布的十二次。每个函数都有额外的函数包装开销,而在我的初始帖子中我只是总结了各行。)

 swap1: 12.547 s
 swap2: 14.301 s
 swap3: 51.739 s

以下是代码:

 methods (Access = public)
     function swap(self, i1, i2)
        swap1(self, i1, i2);
        swap2(self, i1, i2);
        swap3(self, i1, i2);
        self.SwapCount = self.SwapCount + 1;
    end
 end

 methods (Access = private)
    %
    % swap1: stores values in temporary doubles
    %         This has the best performance
    %
    function swap1(self, i1, i2)
        e1 = self.Data(i1);
        e2 = self.Data(i2);
        self.Data(i1) = e2;
        self.Data(i2) = e1;
    end

    %
    % swap2: stores values in a temporary matrix
    %        Marginally slower than swap1
    %
    function swap2(self, i1, i2)
        m = self.Data([i1, i2]);
        self.Data([i2, i1]) = m;
    end

    %
    % swap3: does not use variables for storage.
    %        This has the worst performance
    %
    function swap3(self, i1, i2)
        self.Data([i1, i2]) = self.Data([i2, i1]);
    end


end

答案 1 :(得分:4)

在第一种(慢)方法中,RHS值是一个矩阵,所以我认为MATLAB在创建一个新矩阵来存储这两个元素时会产生性能损失。第二种(快速)方法通过直接处理元素来避免这种情况。

查看MathWorks上的“Techniques for Improving Performance”文章,了解改进MATLAB代码的方法。

答案 2 :(得分:2)

您也可以这样做:

tmp = self.Data(i1);
self.Data(i1) = self.Data(i2);
self.Data(i2) = tmp;

答案 3 :(得分:2)

Zach可能是正确的,因为可能会使矩阵的临时副本执行第一个操作,尽管我猜测在MATLAB中有一些内部优化试图避免这种情况。它可能是您正在使用的MATLAB版本的函数。我在版本7.1.0.246(几年前)中尝试了两种情况,只看到了大约2-2.5的速度差异。

这可能是通过所谓的“循环展开”来提高速度的一个例子。在进行向量运算时,在内部代码中的某个级别,可能存在一个FOR循环,它循环遍历您正在交换的索引。通过在第二个示例中执行标量操作,您可以避免循环中的任何开销。请注意这两个(有些愚蠢)的例子:

vec = [1 2 3 4];

%Example 1:
for i = 1:4,
  vec(i) = vec(i)+1;
end;

%Example 2:
vec(1) = vec(1)+1;
vec(2) = vec(2)+1;
vec(3) = vec(3)+1;
vec(4) = vec(4)+1;

不可否认,简单地使用矢量操作会更容易:

vec = vec+1;

但上述示例仅用于说明目的。当我多次重复每个示例并对其进行计时时,示例2实际上比示例1快一些。对于具有已知编号的小循环(在示例中,仅为4),放弃循环实际上更有效。当然,在这个特定的例子中,上面给出的向量运算实际上是最快的。

我通常遵循这条规则:尝试一些不同的东西,并针对您的具体问题选择最快的。

答案 4 :(得分:2)

这篇文章值得更新,因为JIT编译器现在是一个东西(since R2015b),因此timeitsince R2013b)更可靠的函数计时。

以下是大型数组中元素交换的简短基准测试功能。 我使用术语“直接交换”和“使用临时变量”来分别描述问题中的两种方法。

结果非常惊人,与使用临时变量相比,直接交换2个元素的性能越来越差。

plot

function benchie()
    % Variables for plotting, loop to increase size of the arrays
    M = 15; D = zeros(1,M); W = zeros(1,M);
    for n = 1:M; 
        N = 2^n;
        % Create some random array of length N, and random indices to swap
        v = rand(N,1);
        x = randi([1, N], N, 1);
        y = randi([1, N], N, 1);
        % Time the functions
        D(n) = timeit(@()direct);
        W(n) = timeit(@()withtemp);
    end
    % Plotting
    plot(2.^(1:M), D, 2.^(1:M), W);
    legend('direct', 'with temp')
    xlabel('number of elements'); ylabel('time (s)')

    function direct()
    % Direct swapping of two elements
        for k = 1:N
            v([x(k) y(k)]) = v([y(k)  x(k)]);
        end
    end

    function withtemp()
    % Using an intermediate temporary variable
        for k = 1:N
            tmp = v(y(k));
            v(y(k)) = v(x(k));
            v(x(k)) = tmp;
        end
    end
end