#haskell
#haskell
Вопрос:
Хорошо, может быть, здесь глупый вопрос, но в настоящее время я изучаю haskell, выполняя задачи на projecteuler.net
Я столкнулся с интересным наблюдением и надеялся, что кто-нибудь сможет пролить свет на то, почему все так, как есть.
Для справки, я реализовывал проблему № 29, вот что я придумал
nub $ [ a^^b | a <- [2..100], b <- [2..100] ]
Я заметил ^^
, что использование **
оператора выполняется быстрее, чем ^
для ввода, указанного выше.
Мой вопрос просто, почему? Каждый из этих операторов применяется к разным классам типов. Я предполагаю, что происходят некоторые преобразования типов, но я бы ожидал ^
, что операции будут быстрее, когда кажется, что это на самом деле противоположно.
Спасибо!
Комментарии:
1. Если список не очень короткий (а это не так), не используйте nub . Это O (n ^ 2).
2. В Haskell нет преобразований типов.
Ответ №1:
Все время тратится на nub
. С ^^
помощью и **
вы nub
продолжаете [Double]
. С ^
ним nub
включено [Integer]
, и сравнение больших целых чисел происходит медленнее, чем сравнение двойных чисел.
Комментарии:
1. Правильно,
nub
выполняет всю работу в примере. Спасибо, что прояснили это.
Ответ №2:
**
и ^^
использует Double
, но ^
использует Integer
. Вы действительно не можете сравнить операцию с плавающей запятой с функцией большого целого числа. Взгляните на реализацию ^
.
В вашем коде верно следующее:
**
реализовано аппаратно.^
использует bigInteger
в хвостовом рекурсивном цикле.^^
то же самое, за исключениемDouble
.
Итак, ваши наблюдения об их относительной производительности имеют смысл.
Ответ №3:
То, что я обнаружил, работая над проблемами Project Euler, заключается в том, что типы могут иметь огромное значение для производительности во время выполнения. Например:
foo :: Integral a => a -> a
foo' :: Integer -> Integer
foo'' :: Int -> Int
у всех очень разная производительность. Представьте мое удивление, когда я обнаружил, что простое разрешение компилятору определять наиболее общий тип для foo
, вместо того, чтобы указывать его самостоятельно, приводило к снижению производительности.
Производительность также (очевидно) сильно зависит от вашей среды: выполняется ли компиляция или интерпретация? Оптимизированный или неоптимизированный? Дело в том, что в некоторых случаях у вас могут быть распакованные примитивные Int#
s под обложками, а не упакованные значения, выделенные из кучи… К сожалению, у меня все еще достаточно n00b, что я не знаю, когда вы получите его против другой 🙁
Итак, возможно, это глупый ответ, но если вы используете GHC и знакомы с программированием на C, попробуйте использовать -keep-hc-files
флаг для сравнения промежуточного кода C, сгенерированного при использовании ^
vs ^^
.vs **
.
Комментарии:
1. Вы получите Int#, когда компилятор увидит, что целые числа строго необходимы. Вы можете помочь с шаблонами взрыва, но проверьте ядро, получили ли вы их, и оцените, действительно ли это помогает. С другой стороны, файлы .hc генерируются только незарегистрированными компиляторами (говорится в руководстве пользователя), поэтому у большинства их все равно не будет.