Инструменты производительности для эрланга
При написании функции, подобной факториалу:
fac(Val) when is_integer(Val)->
Visit = fun (X, _F) when X < 2 ->
1;
(X, F) ->
X * F(X -1, F)
end,
Visit(Val, Visit).
нельзя не заметить, что оптимизация хвостового вызова не прямолинейна, однако, записав ее в стиле анализа продолжения:
fac_cps(Val) when is_integer(Val)->
Visit = fun (X, _F, K) when X < 2 ->
K (1);
(X, F, K) ->
F(X-1, F, fun (Y) -> K(X * Y) end)
end,
Visit(Val, Visit, fun (X) -> X end).
Или, возможно, даже нефункционализированный:
fac_cps_def_lambdas({lam0}, X) ->
X;
fac_cps_def_lambdas({lam1, X, K}, Y) ->
fac_cps_def_lambdas(K, X*Y).
fac_cps_def(X) when is_integer(X) ->
fac_cps_def(X, {lam0}).
fac_cps_def(X, K) when X < 2 ->
fac_cps_def_lambdas(K,1);
fac_cps_def(X, K) ->
fac_cps_def(X-1, {lam1, X, K}).
Сроки этих трех реализаций я обнаружил, что время выполнения, как и ожидалось, одинаково.
У меня вопрос, есть ли способ получить более подробные знания, чем этот? Как, например, получить использование памяти для выполнения функции - я вообще избегаю какой-либо стековой памяти?
Каковы стандартные инструменты для проверки такого рода вещей?
Снова возникают вопросы: как измерить высоту стека функций, как определить использование памяти при вызове функции для каждой из них и, наконец, какой из них лучше?
2 ответа
Мое решение состоит в том, чтобы просто проверить код моими глазами. Со временем вы научитесь определять, имеет ли код стиль хвостового вызова. Обычно меня это не особо волнует, если только я не знаю, какой размер структуры, проходящей через этот код, будет огромным.
Это просто по интуиции для меня. Вы можете проверить размер стека процесса с помощью erlang:process_info/2
, Вы можете проверить время выполнения с fprof
, Но я делаю это только в крайнем случае.
Это не отвечает на ваш вопрос, но почему вы написали такой код? Это не очень Erlangy. Как правило, вы не используете явный CPS, если для этого нет особой причины, обычно он не нужен.
Как говорит @IGIVECRAPANSWERS, вы скоро научитесь видеть хвостовые вызовы, и очень мало случаев, когда вы действительно ДОЛЖНЫ его использовать.
РЕДАКТИРОВАТЬ: комментарий к комментарию. Нет, нет прямого способа проверить, использовал ли компилятор LCO или нет. Он делает именно то, что вы говорите, и предполагает, что вы знаете, что вы делаете и почему.:-) Тем не менее, вы можете быть уверены, что это происходит, когда это возможно, но это все. Единственный способ проверить это - посмотреть на размер стека процесса, чтобы увидеть, растет он или нет. К сожалению, если вы ошиблись в нужном месте, процесс может развиваться очень медленно и его будет трудно обнаружить, за исключением длительного периода времени.
Но, опять же, есть очень мало мест, где вам действительно нужно правильно понять LCO.
PS Вы используете термин LCO (Last Call Optimization), который я узнал еще когда-то. Однако теперь "они", похоже, вместо этого используют TCO (Tail Call Optimization). Это прогресс.:-)