52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 315

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 315

Как нам сравнить быстродействие двух алгоримтов? Оценка быстродействия программ, написанных на

Haskell, может таить в себе подвохи. Например если мы запустим оба алгоритма в одной программе, возмож-

но случится такая ситуация, что часть данных, одинаковая для каждого из методов будет вычислена один

раз, а во втором алгоритме переиспользована, и нам может показаться, что второй алгоритм гораздо быстрее

первого. Также необходимо учитывать внешние факторы. Тестовая программа вычисляется на одном ком-

пьютере, и если алгоритмы тестируются в разное время, может статься так, что мы сидели-сидели и ждали

пока тест завершится, в это время работал первый алгоритм, потом нам надоело ждать, мы решили включить

музыку, проверить почту, и второму алгоритмку досталось меньше вычислительных ресурсов. Все эти фак-

торы необходимо учитывать при тестировании. Как раз для этого и существует замечательная бибилиотека

criterion.

Она проводит серию тестов и по ним оценивает показатели быстродействия. При этом учитывается до-

стоверность тестов. По результатам тестирования показатели сверяются между собой, и если разброс оказы-

вается слишком большим, программа сообщает нам: что-то тут не чисто, данным не стоит доверять. Более

того результаты оформляются в наглядные графики, мы можем на глаз оценить распределения и разброс

показателей.

284 | Глава 19: Ориентируемся по карте

Основные типы criterion

Центральным элементом бибилиотеки является класс Benchmarkable. Он объединяет данные, которые

можно тестировать. Среди них чистые функции (тип Pure) и значения с побочными эффектами (тип IO a).

Мы можем превращать данные в тесты (тип Benchmark) с помощью функции bench:

benchSource :: Benchmarkable b => String -> b -> Benchmark

Она добавляет к данным комментарий и превращает их в тесты. Как было отмечено, существует одна

тонкость при тестировании чистых функций: чистые функции в Haskell могут разделять данные между со-

бой, поэтому для независимого тестирования мы оборачиваем функции в специальный тип Pure. У нас есть

два варианта тестирования:

Мы можем протестировать приведение результата к заголовочной нормальной форме (вспомните главу

о ленивых вычислениях):

nf :: NFData b => (a -> b) -> a -> Pure

или к слабой заголовочной нормальной форме:

whnf :: (a -> b) -> a -> Pure

Аналогичные функции (nfIO, whnfIO) есть и для данных с побочными эффектами. Класс NFData обозна-

чает все значения, для которых заголовочная нормальная форма определена. Этот класс пришёл в бибилио-

теку criterion из библиотеки deepseq. Стоит отметить эту бибилотеку. В ней определён аналог функции

seq. Функция seq приводит значения к слабой заголовочной нормальной форме (мы заглядываем вглюбь

значения лишь на один конструктор), а функция deepseq проводит полное вычисление значения. Значение

приводится к заголовочной нормальной форме.

Также нам пригодится функция группировки тестов:

bgroup :: String -> [Benchmark] -> Benchmark

С её помощью мы объединяем список тестов в один, под некоторым именем. Тестирование проводится с

помощью функции defaultMain:

defaultMain :: [Benchmark] -> IO ()

Она принимает список тестов и выполняет их. Выполнение тестов заключается в компиляции програм-

мы. После компиляции мы получим исполняемый файл который проводит тестирование в зависимости от

параметров, указываемых фланами. До них мы ещё доберёмся, а пока опишем наши тесты:

-- | Module: Speed.hs

module Main where

import Criterion.Main

import Control.DeepSeq

import Metro

instance NFData Station where