Ответ 1
hammar прав, важная проблема заключается в том, что компилятор может видеть тип, который lcs
используется при одновременно с тем, что он может видеть код, поэтому он может специализировать код для этого конкретного типа.
Если компилятор не знает тип, в котором должен использоваться код, он не может не только генерировать полиморфный код. И это плохо для производительности - я довольно удивлен, что это только разница в 2 ×. Полиморфный код означает, что для многих операций требуется поиск типа-типа и, по меньшей мере, делает невозможным встроенную функцию поиска или константные размеры (например, для unboxed доступа к массиву/вектору].
Вы не можете получить сопоставимую производительность в одномодовом случае с реализацией и использовать в отдельных модулях, не создавая код, который нуждается в специализации, видимый на используемом сайте (или, если вы знаете необходимые типы на сайте внедрения, специализируясь там, {-# SPECIALISE foo :: Char -> Int, foo :: Bool -> Integer #-}
и т.д.).
Создание кода, видимого на сайте-участнике, обычно выполняется путем раскрытия разворачивания в файле интерфейса посредством маркировки функции {-# INLINABLE #-}
.
Я попытался маркировать функцию с помощью
INLINE
pragma, которая не имела никакого значения для измерения производительности кросс-модуля.
Только маркировка
lcs :: (U.Unbox a, Eq a) => Vector a -> Vector a -> (Vector Int,Vector Int)
lcs a b | (U.length a > U.length b) = lcsh b a True
| otherwise = lcsh a b False
INLINE
или INLINABLE
не имеет никакого значения, конечно, эта функция тривиальна, и компилятор все равно раскрывает ее разворачивание, поскольку он настолько мал. Даже если его разворачивание не было обнаружено, разница не будет измерима.
Вам нужно разоблачить разворачивание функций, выполняющих фактическую работу, по крайней мере, из полиморфных, lcsh
, findSnakes
, gridWalk
и cmp
(cmp
- это тот, который имеет решающее значение здесь, но остальные необходимы, чтобы 1. увидеть, что cmp
необходим, 2. вызовите из них специализированный cmp
).
Создание этих INLINABLE
, различие между случаем отдельного модуля
$ ./diffBench
warming up
estimating clock resolution...
mean is 1.573571 us (320001 iterations)
found 2846 outliers among 319999 samples (0.9%)
2182 (0.7%) high severe
estimating cost of a clock call...
mean is 40.54233 ns (12 iterations)
benchmarking lcs 10
mean: 1.628523 us, lb 1.618721 us, ub 1.638985 us, ci 0.950
std dev: 51.75533 ns, lb 47.04237 ns, ub 58.45611 ns, ci 0.950
variance introduced by outliers: 26.787%
variance is moderately inflated by outliers
и одномодульный случай
$ ./oneModule
warming up
estimating clock resolution...
mean is 1.726459 us (320001 iterations)
found 2092 outliers among 319999 samples (0.7%)
1608 (0.5%) high severe
estimating cost of a clock call...
mean is 39.98567 ns (14 iterations)
benchmarking lcs 10
mean: 1.523183 us, lb 1.514157 us, ub 1.533071 us, ci 0.950
std dev: 48.48541 ns, lb 44.43230 ns, ub 55.04251 ns, ci 0.950
variance introduced by outliers: 26.791%
variance is moderately inflated by outliers
имеет малую величину.