Центральным элементом бибилиотеки является класс 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
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 оценит