Низкая производительность Matlab с использованием замыканий

Я кодирую решение уравнения Пуассона на двумерном прямоугольнике с использованием конечных элементов. Чтобы упростить код, я сохраняю дескрипторы базовых функций в массиве, а затем перебираю эти базовые функции, чтобы создать свою матрицу и правую часть. Проблема в том, что даже для очень грубых сеток это слишком медленно. Для сетки 9x9 (с использованием Dirichlet BC необходимо найти 49 узлов) это занимает около 20 секунд. Используя профиль, я заметил, что около половины времени тратится на доступ (а не на выполнение) моих базовых функций.

Профилировщик говорит matrix_assembly>@(x,y)bilinearBasisFunction(x,y,xc(k-1),xc(k),xc(k+1),yc(j-1),yc(j),yc(j+1)) (156800 calls, 11.558 sec), собственное время (без выполнения билинейного базового кода) составляет более 9 секунд. Есть идеи, почему это может быть так медленно?

Вот часть кода, при необходимости я могу опубликовать еще:

%% setting up the basis functions, storing them in cell array
basisFunctions = cell(nu, 1); %nu is #unknowns 
i = 1;
for j = 2:length(yc) - 1
for k = 2:length(xc) - 1
    basisFunctions{i} = @(x,y) bilinearBasisFunction(x,y, xc(k-1), xc(k),...
        xc(k+1), yc(j-1), yc(j), yc(j+1)); %my code for bilinear basis functions
    i = i+1;
end
end

%% Assemble matrices and RHS
M = zeros(nu,nu);
S = zeros(nu,nu);
F = zeros(nu, 1);

for iE = 1:ne
for iBF = 1:nu
    [z1, dx1, dy1] = basisFunctions{iBF}(qx(iE), qy(iE));

    F(iBF) = F(iBF) + z1*forcing_handle(qx(iE),qy(iE))/ae(iE);

    for jBF = 1:nu
        [z2, dx2, dy2] = basisFunctions{jBF}(qx(iE), qy(iE));

        %M(iBF,jBF) = M(iBF,jBF) + z1*z2/ae(iE);
        S(iBF,jBF) = S(iBF, jBF) + (dx1*dx2 + dy1*dy2)/ae(iE);
    end        
end
end

person Lukas Bystricky    schedule 20.11.2013    source источник
comment
Для начала, вы вызываете эту строку 156 800 раз (создавая столько анонимных функций) в двойном цикле for и изменяете значения параметров, используя каждый раз индексацию! И вы уверены, что nu равно (length(xc)-2)*(length(yc)-2), чтобы избежать перераспределения в массиве basisFunctions ячеек?   -  person horchler    schedule 20.11.2013
comment
Есть ли смысл иметь 156800 функций вместо одной функции с параметрами (jBF,qx(iE), qy(iE))?   -  person Daniel    schedule 20.11.2013
comment
@DanielR: всего 49 функций, каждая с немного разными параметрами. Конечно, есть и другие способы сделать это, но то, как я это сделал здесь, имеет то преимущество, что он очень удобочитаем, и я не понимаю, почему он должен быть намного медленнее.   -  person Lukas Bystricky    schedule 20.11.2013
comment
Тогда что было сделано 156 800 раз? matrix_assembly - это имя подфункции в bilinearBasisFunction?   -  person horchler    schedule 20.11.2013
comment
@horchler: да, я часто это называю, но это неизбежно, мне действительно нужно много оценивать свои базовые функции. Я также вызываю bilinearBasisFunction 156800 раз, но, согласно профилировщику, это занимает всего 2 секунды.   -  person Lukas Bystricky    schedule 20.11.2013
comment
@horchler: насколько я понимаю, я обращаюсь к массиву 156800 раз, и это узкое место. Однако я мог неправильно интерпретировать профилировщик.   -  person Lukas Bystricky    schedule 20.11.2013
comment
Чтобы сказать что-нибудь, нам нужно увидеть matrix_assembly и, может быть, bilinearBasisFunction тогда. Даже если каждый вызов занимает несколько десятых миллисекунд, в сумме будет 156 800 вызовов функций.   -  person horchler    schedule 20.11.2013
comment
@horchler: Я должен был прояснить это, но matrix_assembly - это код, который я опубликовал. На самом деле это не так много, кроме решения системы. ne - количество элементов (64 для сетки 9x9), так что это большинство из 156800 вызовов (64 * 49 * 49).   -  person Lukas Bystricky    schedule 20.11.2013
comment
Если это не домашнее задание или ваше собственное назидание, вы можете проверить, есть ли у вас уравнение в частных производных набор инструментов (у меня он есть с моей институциональной версией R2013a) и прочтите эту статью.   -  person horchler    schedule 20.11.2013
comment
@horchler: Спасибо, но я делаю это в образовательных целях.   -  person Lukas Bystricky    schedule 20.11.2013


Ответы (1)


Попробуйте изменить basisFunctions из массива ячеек в обычный массив.

Вы также можете попытаться встроить прямой вызов bilinearBasisFunction в цикл jBF, а не использовать basisFunctions. Создание и последующее использование анонимных функций в Matlab всегда медленнее, чем прямое использование целевой функции. Таким образом код может быть немного более подробным, но будет быстрее.

person Yair Altman    schedule 21.11.2013