бой, поэтому для независимого тестирования мы оборачиваем функции в специальный тип 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
rnf (St a b) = rnf (rnf a, rnf b)
instance NFData Way
where
instance NFData Name where
pair1 = (St Orange DnoBolota, St Green Prizrak)
pair2 = (St Red Lao, St Blue De)
test name search = bgroup name $ [
bench ”1” $ nf (uncurry search) pair1,
bench ”2” $ nf (uncurry search) pair2]
main = defaultMain [
test ”Set”
connectSet,
test ”Hash” connectHashSet]
Оценка быстродействия с помощью criterion | 285
Экземпляр для класса NFData похож на экземпляр для Hashable. Мы также определили метод значения
через методы для типов, из которых он состоит. Класс NFData устроен так, что для типов из класса Enum мы
можем воспользоваться определением по умолчанию (как в случае для Way и Name).
Теперь перейдём в командную строку, переключимся на директорию с нашим модулем и скомпилируем
его:
$ ghc -O --make Speed.hs
Флаг -O говорит ghc, что не обходимо провести оптимизацию кода. Появится исполняемый файл Speed.
Что мы можем делать с этим файлом? Узнать это можно, запустив его с флагом –help:
Мы можем узнать какие функции нам доступны, набрав:
$ ./Speed --help
I don’t know what version I am.
Usage: Speed [OPTIONS] [BENCHMARKS]
-h, -?
--help
print help, then exit
-G
--no-gc
do not collect garbage between iterations
-g
--gc
collect garbage between iterations
-I CI
--ci=CI
bootstrap confidence interval
-l
--list
print only a list of benchmark names
-o FILENAME
--output=FILENAME
report file to write to
-q
--quiet
print less output
--resamples=N
number of bootstrap resamples to perform
-s N
--samples=N
number of samples to collect
-t FILENAME
--template=FILENAME
template file to use
-u FILENAME
--summary=FILENAME
produce a summary CSV file of all results
-V
--version
display version, then exit
-v
--verbose
print more output
If no benchmark names are given, all are run
Otherwise, benchmarks are run by prefix match
Из этих настроек самые интресные, это -s и -o. -s указывает число сэмплов выборке (столько раз будет
запущен каждый тест). а -o говорит, о том в какой файл поместить результаты. Результаты представлены в
виде графиков, формируется файл, который можно открыть в любом браузере. Записать данные в таблицу
(например для отчёта) можно с помощью флага -u.
Проверим результаты:
./Speed -o res. html -s 100
Откроем файл res. html и посмотрим на графики. Оказалось, что для данных двух случаев первый алго-
ритм работал немного лучше. Но выборку из двух вариантов вряд ли можно считать убедительной. Давайте
расширим выборку с помощью QuickCheck. Мы запустим проверку какого-нибудь свойства тем и другим
методом. В итоге QuickCheck сам сгенерирует достаточное число случайных данных, а criterion оценит
быстродействие. Мы проверим самое первое свойство (о перевёрнутых маршрутах) на том и другом алгорит-
ме.
module Main where