Медленная анонимная функция - PullRequest
6 голосов
/ 15 апреля 2011

Предположим, у вас есть цикл с 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 ]

5 голосов
/ 15 апреля 2011

Ну, можно ожидать, что каждая функция, даже анонимная функция , будет иметь некоторое количество дополнительных издержек, связанных с ее вызовом, что делает их немного медленнее, чем их аналоги из однострочных выражений в вашем примере. Однако в этом случае может быть лишним из-за того, что функции с именем eq уже существуют в изобилии в MATLAB, так как eq является именем метода перегруженного == оператор . Используя команду WHICH примерно так:

>> which eq -all

Покажет вам, что eq сильно перегружен перегружен, причем один существует для каждого из основных типов данных и большинства объектов.

Я бы попробовал использовать другое имя для вашего дескриптора анонимной функции, просто чтобы посмотреть, может ли отправка быть фактором, хотя я сомневаюсь, что это основано на порядке приоритета функции (т.е. переменные всегда имеют приоритет). Ваше лучшее решение с точки зрения производительности может заключаться в том, чтобы избежать дополнительных затрат на вызов функции, выполнив что-то вроде того, что DavW предлагает .

Я хотел бы сделать еще одно предложение. Многие математические операции, которые вы выполняете, можно значительно улучшить, чтобы сделать их более эффективными, в частности, с помощью функции MEAN и оператора двоеточия для преобразования всей матрицы в столбец вектор:

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 для моей переменной, поскольку оно уже используется для встроенной функции , и вы определенно не хотите тень это.

5 голосов
/ 15 апреля 2011

Наличие переключателя внутри цикла выполняет сравнение 50000 раз, которое нужно выполнить только один раз, против чего я бы посоветовал.

Второе немного более тонкое, но вполне вероятно, что уравнениеФункция динамически просматривается на каждой итерации и, возможно, также интерпретируется каждый раз (не знаю, как MATLAB выполняет оптимизацию).Лучше всего делать ставку на производительность, вероятно, поместив цикл for в коммутатор

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