缓慢的匿名功能

时间:2011-04-15 15:29:45

标签: matlab performance anonymous-function

假设你有一个50000次迭代的循环,并希望从很多矩阵中计算平均值(标量)。这不完整,但大致如下:

for k=1:50000
...
mean=sum(sum(matrix))/numel(matrix); %Arithmetic mean
...
end

现在想要包括不同的均值方程式可供选择。首先我尝试了这个:

average='arithmetic'
for k=1:50000
...
switch average
    case 'arithmetic'
        mean=sum(sum(matrix))/numel(matrix); %Arithmetic mean
    case 'geometric'
        mean=prod(prod(matrix)).^(1/numel(matrix)); %Geometric mean
    case 'harmonic'
        mean=numel(matrix)/sum(sum(1./matrix)); %Harmonic mean
end
...
end

这显然比第一个循环慢很多,因为它需要为每次迭代找到匹配的字符串,感觉真的没必要。然后我尝试了这个:

average='arithmetic'
switch average
    case 'arithmetic'
        eq=@(arg)sum(sum(arg))/numel(arg); %Arithmetic mean
    case 'geometric'
        eq=@(arg)prod(prod(arg)).^(1/numel(arg)); %Geometric mean
    case 'harmonic'
        eq=@(arg)numel(arg)/sum(sum(1./arg)); %Harmonic mean
end

for k=1:50000
...
mean=eq(matrix); %Call mean equation
...
end

这仍然是第一个循环的两倍慢,我不明白为什么。最后两个循环的速度几乎相似。

我在这里做错了吗?如何通过这个额外功能实现与第一个循环相同的性能?

非常感谢帮助!

2 个答案:

答案 0 :(得分:5)

将开关置于循环内是执行50000次比较,只需要执行一次,这是我建议的。

第二个是更微妙的,但很有可能每次迭代都会动态查找eq函数,并且每次都可能解释(不确定MATLAB如何进行优化)。对性能的最佳选择可能是将for循环置于开关内

switch average
    case 'arithmetic'
        for ... end
    case 'geometric'
        for ... end
    case 'harmonic'
        for ... end
end

答案 1 :(得分:5)

好吧,每个函数,甚至anonymous functions,都可能会在调用它时遇到一些额外的开销,使它们比你的示例中的单行表达式稍微慢一些。但是,在这种情况下,可能可能是额外的开销,因为名称eq的函数已经在MATLAB中大量存在,因为eq is the method name of the overloaded == operator。使用WHICH命令如下:

>> which eq -all

将向您显示eq 严重重载,其中每个基本数据类型和大多数对象都存在。

我会尝试为您的匿名函数句柄使用不同的名称,只是为了查看调度是否是一个因素,尽管我有点怀疑它基于function precedence order(即变量总是出现优先考虑)。性能方面的最佳解决方案可能是通过执行DavW suggests之类的操作来避免额外的函数调用开销。

我想提出另一个建议。您正在进行的许多数学运算可以大大提高,以提高它们的效率,特别是通过使用函数MEANcolon operator将整个矩阵重新整形为列向量:

result = mean(matrix(:));  %# For the arithmetic mean

result = prod(matrix(:))^(1/numel(matrix));  %# For the geometric mean

result = 1/mean(1./matrix(:));  %# For the harmonic mean

请注意,我没有为我的变量使用名称mean,因为它已经用于built-in function,你绝对想要遮挡它