Рассмотрим следующий простой тест скорости arrayfun
:
T = 4000;
N = 500;
x = randn(T, N);
Func1 = @(a) (3*a^2 + 2*a - 1);
tic
Soln1 = ones(T, N);
for t = 1:T
for n = 1:N
Soln1(t, n) = Func1(x(t, n));
end
end
toc
tic
Soln2 = arrayfun(Func1, x);
toc
На моей машине (Matlab 2011b на Linux Mint 12) результат этого теста:
Elapsed time is 1.020689 seconds.
Elapsed time is 9.248388 seconds.
Что за?!? arrayfun
хотя и выглядит более чистым, но на порядок медленнее. Что здесь происходит?
Кроме того, я проделал похожий стиль тестирования cellfun
и обнаружил, что он примерно в 3 раза медленнее, чем явный цикл. Опять же, этот результат противоположен тому, что я ожидал.
Мой вопрос: Почему arrayfun
и cellfun
так много медленнее? И, учитывая это, есть ли какие-нибудь веские причины использовать их (кроме как для того, чтобы код выглядел хорошо)?
Примечание: здесь я говорю о стандартной версии arrayfun
, а НЕ о версии для графического процессора из набора инструментов параллельной обработки.
РЕДАКТИРОВАТЬ: Чтобы быть ясным, я знаю, что Func1
выше можно векторизовать, как указано Оли. Я выбрал его только потому, что он дает простой тест скорости для целей фактического вопроса.
РЕДАКТИРОВАТЬ: Следуя предложению grungetta, я повторно провел тест с feature accel off
. Результат:
Elapsed time is 28.183422 seconds.
Elapsed time is 23.525251 seconds.
Другими словами, может показаться, что большая часть разницы заключается в том, что ускоритель JIT выполняет гораздо лучшую работу по ускорению явного for
цикла, чем он arrayfun
. Мне это кажется странным, поскольку на arrayfun
самом деле предоставляет больше информации, т. Е. Его использование показывает, что порядок вызовов Func1
не имеет значения. Кроме того, я заметил, что независимо от того, включен ли JIT-ускоритель или выключен, моя система всегда использует только один процессор ...
источник
Ответы:
Вы можете получить представление, запустив другие версии вашего кода. Рассмотрите возможность явной записи вычислений вместо использования функции в вашем цикле
Время вычислить на моем компьютере:
Теперь, хотя полностью «векторизованное» решение явно является самым быстрым, вы можете видеть, что определение функции, которая будет вызываться для каждой записи x, - это огромные накладные расходы. Просто явная запись вычислений дала нам ускорение в 5 раз. Я думаю, это показывает, что JIT-компилятор MATLABs не поддерживает встроенные функции . Согласно ответу gnovice там на самом деле лучше написать нормальную функцию, чем анонимную. Попытайся.
Следующий шаг - удалить (векторизовать) внутренний цикл:
Еще один фактор 5 ускорения: в этих утверждениях есть что-то, что вам следует избегать циклов в MATLAB ... Или это действительно так? Посмотри на это тогда
Намного ближе к «полностью» векторизованной версии. Matlab хранит матрицы по столбцам. Вы всегда должны (по возможности) структурировать свои вычисления так, чтобы они были векторизованы «по столбцам».
Теперь мы можем вернуться к Soln3. Порядок циклов там «построчный». Давай изменим это
Лучше, но все равно очень плохо. Одиночная петля - хорошо. Двойная петля - плохо. Я предполагаю, что MATLAB проделал приличную работу по повышению производительности циклов, но накладные расходы на цикл все же присутствуют. Если бы у вас внутри была более тяжелая работа, вы бы этого не заметили. Но поскольку это вычисление ограничено пропускной способностью памяти, вы видите накладные расходы цикла. И вы будете еще более ясно увидеть накладные расходы вызова FUNC1 там.
Так что случилось с arrayfun? Там тоже нет функции inlinig, поэтому много накладных расходов. Но почему это намного хуже, чем двойной вложенный цикл? На самом деле, тема использования cellfun / arrayfun широко обсуждалась много раз (например, здесь , здесь , здесь и здесь ). Эти функции просто медленные, вы не можете использовать их для таких мелкозернистых вычислений. Вы можете использовать их для краткости кода и необычных преобразований между ячейками и массивами. Но функция должна быть тяжелее того, что вы написали:
Обратите внимание, что Soln7 теперь является ячейкой ... иногда это полезно. Производительность кода сейчас неплохая, и если вам нужна ячейка в качестве вывода, вам не нужно преобразовывать матрицу после использования полностью векторизованного решения.
Так почему же arrayfun медленнее, чем простая циклическая структура? К сожалению, мы не можем сказать наверняка, поскольку исходный код недоступен. Вы можете только догадываться, что, поскольку arrayfun - это функция общего назначения, которая обрабатывает все виды различных структур данных и аргументов, она не обязательно очень быстрая в простых случаях, которые вы можете напрямую выразить как вложения циклов. Откуда берутся накладные расходы, мы не можем знать. Можно ли избежать накладных расходов за счет лучшей реализации? Может быть нет. Но, к сожалению, единственное, что мы можем сделать, - это изучить производительность, чтобы определить, в каких случаях она работает хорошо, а в каких - нет.
Обновление Поскольку время выполнения этого теста невелико, для получения надежных результатов я добавил цикл вокруг тестов:
Несколько раз указано ниже:
Вы видите, что arrayfun по-прежнему плохой, но как минимум не на три порядка хуже, чем векторизованное решение. С другой стороны, один цикл с вычислениями по столбцам выполняется так же быстро, как и полностью векторизованная версия ... Все это было сделано на одном процессоре. Результаты для Soln5 и Soln7 не меняются, если я переключаюсь на 2 ядра - в Soln5 мне пришлось бы использовать parfor для его распараллеливания. Забудьте об ускорении ... Soln7 не работает параллельно, потому что arrayfun не работает параллельно. С другой стороны, векторизованная версия Olis:
источник
cellfun
был реализован как MEX-файл (с исходным кодом C, доступным рядом). На самом деле это было довольно просто. Конечно, он поддерживал только применение одной из 6 жестко запрограммированных функций (вы не могли передать дескриптор функции, только строку с одним именем функции)Это потому что !!!!
не
gpuarray
тип;Все, что вам нужно сделать, это
источник
gpuarray
. Почти наверняка поэтому этот ответ был отвергнут.gpuarray
поддерживается только для видеокарт nVidia. Если у них нет такого оборудования, то ваш совет (или его отсутствие) не имеет смысла. -1