[haskell] загадочное время исполнения
Что касается eval1 и eval2, то такое поведение логично. Монада выполнение не ускоряет, при каждой монадной операции имеется оверхед. И еще надо посмотреть, что из себя представляет Reader, вроде, не сильно быстро работает по определению (помню, где-то предлагали вместо него parsec применять).
Почему compile1 и compile2 должны работать быстрее, не понял. Тебе еще повезло, что они так же как eval2 работают. Ведь по сути это одно и тоже, только первые специально переусложнены.
Почему compile1 и compile2 должны работать быстрее, не понял.Потому что убирается конструкция case. То есть, ветвления заменяются косвенными вызовами. По идее, это от архитектуры процессора должно зависеть сильно.
> по определению (помню, где-то предлагали вместо него parsec применять).
Parsec вместо Reader? Это как?
По определению, Reader e a = e -> a. То есть просто, добавляется один параметр в функцию. По идее eval1 и eval2 должны превращаться в одно и тоже после syntactic desugaring.
> идее, это от архитектуры процессора должно зависеть сильно.
IMHO тут ситуация такая
код типа
switch (tag) {
case 1: r = f(x); break;
case 2: r = g(y); break;
заменяется на
r = *h(x);
То есть (case + прямой вызов) на (косвенный вызов). Насколько я понимаю, второе всегда быстрее первого. Можешь привести пример архитектуры, где это не так?
Судя по тому, что времена выполнения очень близки, дело, наверное, всё-таки не в инструкциях. Видимо, какой-то ещё оверхед превышает эту разницу во много раз. Может, управление памятью и boxing?
> Видимо, какой-то ещё оверхед превышает эту разницу во много раз.
Наверное. Есть какие-то методы это проверить/измерить?
> Может, управление памятью и boxing?
Заменил Int в Int# в половине мест. В остальной половине пока не понял как — на UArray Int# Int# компилятор ругается. Время уменьшилось с 3.71 до 3.58, разница осталась такого же порядка. Надо как-то переписать доступ к переменным и do_test, чтобы без списков обходился.
Фишка может быть в том, что Хаскель сам за счет ленивости реализует поведение compile. (eval2 e) самостоятельный редекс, который будет адаптирован под е. Надо бы тоже самое на OCaml написать, тогда этот вопрос прояснится, но у меня его сейчас нет. Или на Хаскеле вызывать eval2 e env одним выражением для всех env.
Оставить комментарий
Landstreicher
Я написал четыре вариант вычисления простых арифметических выражений с переменными.- eval1 - простой вычислитель на основе монад;
- eval2 - простое вычисление выражения "в лоб";
- compile1 - попытка сделать компиляцию для ускорения;
- compile2 - попытка слегка соптимизировать compile2.
Основной интерес представляло сравнение скорости их работы. Для этого значения выражений вычислялись 1000000 различных наборах переменных. Вот результаты измерений:
- eval1 - 17.47 с
- eval2 - 3.71 с
- compile1 - 3.79 с
- compile2 - 3.74 с
Эти числа поставили меня в тупик.
1) Предполагалось, что eval1 и eval2 будут работать примерно одинаково. В действительности, eval1 в 4.7 раза медленнее, чем eval2.
2) Предполагалось, что compile{1,2} будут работать значительно быстрее, чем eval{1,2} (компиляция vs интерпретация). На практике, никакого ускорения не было обнаружено.
Кто-нибудь может объяснить эти результаты?
Использовался GHC 6.6 on Linux-2.6/Athlon XP/Debian Etch.
Для сравнения - эквивалентная программа на C++ работает за 1.04 с.