LINUX.ORG.RU

Хвостовая рекурсия.

 


2

3

Разбираюсь с хвостовой рекурсией.

Реализовал алгоритм поиска пирамидального числа:

-- recursion version
--
fermaPyr 0 = 0
fermaPyr n = fermaTria n + fermaTria (n - 1)

fermaTria 0 = 0
fermaTria n = n + fermaTria (n - 1)

-- tail recursion version
-- 
fermaPyrAcc n = let fAcc 0 a = a
                    fAcc n a = fAcc (n - 1) (fermaTriaAcc n + a)
                in fAcc n 0

fermaTriaAcc n = let fAcc 0 a = a
                     fAcc n a = fAcc (n - 1) (n + a)
                 in fAcc n 0

-- tail recursion ver.2
--
fermaPyrAcc2 0 = 0
fermaPyrAcc2 n = fermaTriaAcc n + fermaTriaAcc (n - 1)



main = do
    let a = fermaPyr 1000000
        b = fermaPyrAcc 10000
        c = fermaPyrAcc2 1000000
    putStrLn $ show a
    --putStrLn $ show b
    --putStrLn $ show c

Запуск варианта а:

     177,963,104 bytes allocated in the heap
     187,666,896 bytes copied during GC
      36,388,928 bytes maximum residency (8 sample(s))
          36,384 bytes maximum slop
              82 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       237 colls,     0 par    0.13s    0.13s     0.0005s    0.0014s
  Gen  1         8 colls,     0 par    0.09s    0.10s     0.0119s    0.0285s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.10s  (  0.10s elapsed)
  GC      time    0.22s  (  0.22s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.01s  (  0.01s elapsed)
  Total   time    0.33s  (  0.33s elapsed)

  %GC     time      67.7%  (67.7% elapsed)

  Alloc rate    1,831,837,945 bytes per MUT second

  Productivity  32.3% of total user, 31.9% of total elapsed

Всё нормально, расход памяти, как и ожидалось, немалый, время исполнения тоже.

Запускаю вариант b:

   3,201,017,336 bytes allocated in the heap
         697,536 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0      6104 colls,     0 par    0.03s    0.03s     0.0000s    0.0000s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    1.96s  (  1.98s elapsed)
  GC      time    0.03s  (  0.03s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    1.99s  (  2.01s elapsed)

  %GC     time       1.7%  (1.7% elapsed)

  Alloc rate    1,635,694,726 bytes per MUT second

  Productivity  98.3% of total user, 97.4% of total elapsed

Здесь, памяти расходуется гораздо меньше, но время исполнения… при аргументе на 2 порядка меньше, чем в других вариантах, работает в несколько раз медленнее. Увеличив аргумент на порядок, я уже не дожидался окончания расчётов.

Вариант с:

     128,057,440 bytes allocated in the heap
          29,752 bytes copied during GC
          46,240 bytes maximum residency (2 sample(s))
          23,392 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0       243 colls,     0 par    0.00s    0.00s     0.0000s    0.0001s
  Gen  1         2 colls,     0 par    0.00s    0.00s     0.0001s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.08s  (  0.09s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  RP      time    0.00s  (  0.00s elapsed)
  PROF    time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.08s  (  0.09s elapsed)

  %GC     time       2.0%  (2.1% elapsed)

  Alloc rate    1,571,481,972 bytes per MUT second

  Productivity  97.8% of total user, 94.8% of total elapsed

Самый быстрый и меньше всего расходует память.

Для меня не очевидна разница между вариантами b и с. Почему оно так? Я ожидал, что вариант b будет самым лучшим т.к. в функции fermaPyrAcc2 нет хвостовой рекурсии, она только в её внутреннем цикле.


Ответ на: комментарий от MyTrooName

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

ghc -O2 -prof -rtsopts 1.2.hs -auto -fforce-recomp
HolyBoy
() автор топика

criterion придумали для ленивых?

qnikst ★★★★★
()
Ответ на: комментарий от Waterlaz

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

qnikst ★★★★★
()
Ответ на: комментарий от Waterlaz

а какое вообще определение у числа, а то все ресурсы где я вижу имеют и явную нерекурсивную формулу для вычисления..

qnikst ★★★★★
()

Не, вот чё в ней разбираться? Рекурсивный вызов должен быть последним, что функция посчитает

vonenij
()
Ответ на: комментарий от qnikst

Если я правильно понимаю, то количество шариков из которых состоит треугольная пирамида с n шариками на ребре.

1+(1+2)+(1+2+3)+(1+2+3+4)+...+(1+2+3+..+n)

Waterlaz ★★★★★
()

Если хочешь, чтобы второй вариант работал быстро, то

import Data.Int

fermaPyrAcc :: Int64 -> Int64

fermaTriaAcc :: Int64 -> Int64

На 10000 еще помещается.

Waterlaz ★★★★★
()

Я ожидал, что вариант b будет самым лучшим

Учитывая, что первый и третий варианты неправильные, он действительно самый лучший.

Miguel ★★★★★
()
Ответ на: комментарий от Waterlaz

просто из его кода это было совсем не очевидно..

спасибо за разъяснения.

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от qnikst

Есть т.н. треугольные числа:

f (n) = sum j , где j=1…n.

Так что,

f(1) = 1 f(2) = 3 f(3) = 6 f(4) = 10

и т.д., т.е. n-ное число — это сумма n-первых натуральных целых чисел.

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

g (n) = sum f (j) , где j=1…n

Иными словами, пирамидальное число — это сумма n-первых треугольных чисел.

Функции fermaTria и fermaTriaAcc — соответственно, рекурсивный и хвосторекурсивный варианты нахождения треугольного числа. fermaPyr, fermaPyrAcc2 и fermaPyrAcc — пирамидального.

Варианты a,b,c — разные способы комбинирования рекурсивных и хвосторекурсивных функций.

Я специально в стартовом посте упомянул про вариант b, повторю здесь ещё раз:

но время исполнения… при аргументе на 2 порядка меньше, чем в других вариантах, работает в несколько раз медленнее.

Функции fermaPyr и fermaPyrAcc2 работают верно и выдают верный результат.

С функцией fermaPyrAcc и в самом деле не так что-то. Неверный результат вычислений и они идут слишком долго. Думаю.

HolyBoy
() автор топика
Последнее исправление: HolyBoy (всего исправлений: 1)
Ответ на: комментарий от qnikst

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

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Да, я лопухнулся: вместо рекурсивного вызова нужной функции вызывал стороннюю.

Исправил:

Теперь, всё работает как положено: fermaPyr жрёт память, работает дольше всего; fermaPyr ест меньше всего памяти, работает быстрее всего; fermaPyr2 — промежуточный вариант.

Теперь всё логично, как и ожидалось.

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

перепиши их, плз, почеловечески, в таком виде в коде разбираться не очень хочется.. и для меряния скорости используй criterion.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

потому, что это блин не смешно даже

benchmarking fermaPyrAcc
mean: 677.7313 us, lb 677.3967 us, ub 678.2646 us, ci 0.950
std dev: 2.129318 us, lb 1.499862 us, ub 3.290286 us, ci 0.950

benchmarking fermaPyr'
mean: 3.747422 us, lb 3.737614 us, ub 3.753084 us, ci 0.950
std dev: 37.41584 ns, lb 20.82936 ns, ub 62.17786 ns, ci 0.950

benchmarking fermaPyr''
mean: 3.500755 us, lb 3.497444 us, ub 3.508952 us, ci 0.950
std dev: 25.00996 ns, lb 12.05002 ns, ub 50.49977 ns, ci 0.950

разница твоего лучшего решения и аккуратно написанных на 2 порядка.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst
-- recursion version
--
fermaPyr 0 = 0
fermaPyr n = fermaTria n + fermaPyr (n - 1)

-- tail recursion version
-- 
fermaPyr' n = let fAcc 0 a = a
                  fAcc n a = fAcc (n - 1) (fermaTria' n + a)
              in fAcc n 0
    
-- tail recursion ver.2
--
fermaPyr'' 0 = 0
fermaPyr'' n = fermaTria' n + fermaPyr'' (n - 1)


-- recursion version
-- 
fermaTria 0 = 0
fermaTria n = n + fermaTria (n - 1)

-- tail recursion version
fermaTria' n = let fAcc 0 a = a
                   fAcc n a = fAcc (n - 1) (n + a)
               in fAcc n 0

main = do
    let a = fermaPyr 10000
        b = fermaPyr' 10000
        c = fermaPyr'' 10000
    putStrLn $ show a
    --putStrLn $ show b
    --putStrLn $ show c

Вот так лучше?

Смысл этого упражнения был в том, чтобы понять, как работает хвостовая рекурсия.

HolyBoy
() автор топика
Последнее исправление: HolyBoy (всего исправлений: 2)
Ответ на: комментарий от HolyBoy

лучше убрать let в where и можно параметры местами поменять:

fermaTriaAcc' :: Int -> Int
fermaTriaAcc' = fAcc 0
  where
    fAcc !a 0 = a
    fAcc !a n = fAcc (n + a) (n - 1)

! для добавления строгости, но рекурсивному варианту, даже и хвостово это не поможет :)

qnikst ★★★★★
()
Ответ на: комментарий от HolyBoy

техника называется tying the knot, и позволяет получить мемоизированный результат нахаляву, а вместе с deforestation (техника уничтожения промежуточных списков) в итоге получается очень неплохой код.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Понимаю, что не поможет. Цель была не «эффективно решить», а «посмотреть, что будет, если…». И на пути к ней появился вопрос, уже закрытый.

Касаемо твоего кода.

Что означает p1 = 0:zipWith (+) p1 [1..] — понятно: объединение в списке 0 и суммы бесконечных списков [1..] и p1. Непонятно, как p1 в этом участвует.

Код

> let p3 = 0:zipWith (+) [1..] [1..]
> take 10 p3
[0,2,4,6,8,10,12,14,16,18]

понятен. А вот как несуществующий поначалу список p1 суммируется с [1..] — загадка:

> let p1 = 0:zipWith (+) p1 [1..]
> take 10 p1
[0,1,3,6,10,15,21,28,36,45]
HolyBoy
() автор топика
Ответ на: комментарий от qnikst

> tying the knot

Пошёл искать. Спасибо ещё раз!

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

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

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

fermaPyr' 100000:

2 MB total memory in use (0 MB lost due to fragmentation) Total time 196.96s (197.61s elapsed)

fermaPyr''' :: Int -> Int
fermaPyr''' n = (scanl (+) 0 $ scanl1 (+) [0..]) !! n

при n=100000

10 MB total memory in use (0 MB lost due to fragmentation) Total time 205.40s (205.98s elapsed)

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

OK, посмотрю завтра, как до компа доберусь, есть подозрение, что это лечится весьма просто.

По поводу p1 = 0: zip with (+) p1 (0:p1) тут все дело в ленивости, чтобы разобраться самый простой вариант это взять и аккуратно расписать, что происходит, искать презентацию где я расписывал это подробно для Фибоначчи или расписывать с телефона мне лень, так что напишу только идею. Поскольку хацкель это ленивый язык, то вычислять весь p1 он не будет, соотв у тебя будет список вида 0:<thunk>, но для вычисления следующего элемента этого достаточно т.к. для вычисления второго элемента используется только уже вычисленный первый, для вычисления третьего уже вычесленный к тому моменту второй и т.д.

qnikst ★★★★★
()
Ответ на: комментарий от HolyBoy

А тут есть подозрение, что (+) не хватает строгости и нужно написать, что нить типа (force.). (+)

qnikst ★★★★★
()
Ответ на: комментарий от HolyBoy

не представляю, как ты этого добиваешься, какая хоть версия ghc, и что если профилировку отключить?

для 10000

benchmarking fermaPyrAcc
collecting 100 samples, 1 iterations each, in estimated 6.865811 s
mean: 68.66849 ms, lb 68.63965 ms, ub 68.79752 ms, ci 0.950
std dev: 265.9661 us, lb 37.34891 us, ub 627.9339 us, ci 0.950

benchmarking fermaPyr'
mean: 42.34904 us, lb 42.22745 us, ub 42.56601 us, ci 0.950
std dev: 805.2933 ns, lb 539.4956 ns, ub 1.446515 us, ci 0.950
found 2 outliers among 100 samples (2.0%)
  1 (1.0%) high severe
variance introduced by outliers: 12.270%
variance is moderately inflated by outliers

benchmarking fermaPyr''
mean: 42.16785 us, lb 42.07963 us, ub 42.28706 us, ci 0.950
std dev: 524.6303 ns, lb 412.9531 ns, ub 732.7192 ns, ci 0.950

для 100000:

benchmarking fermaPyr'
collecting 100 samples, 1 iterations each, in estimated 7.799315 s
mean: 613.8454 us, lb 612.1216 us, ub 616.7517 us, ci 0.950
std dev: 11.19240 us, lb 7.810609 us, ub 19.73199 us, ci 0.950
found 18 outliers among 100 samples (18.0%)
  11 (11.0%) high mild
  7 (7.0%) high severe
variance introduced by outliers: 11.323%
variance is moderately inflated by outliers

benchmarking fermaPyr''
mean: 619.0215 us, lb 616.2010 us, ub 622.8433 us, ci 0.950
std dev: 16.62720 us, lb 13.37853 us, ub 21.14115 us, ci 0.950
found 10 outliers among 100 samples (10.0%)
  6 (6.0%) high mild
  4 (4.0%) high severe

твой вариант не мерял, т.к. ждать слишком долго.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Glasgow Haskell Compiler, Version 7.6.3, stage 2 booted by GHC version 7.6.3 у меня. Собрал без -prof и стало быстрее, да, для

fermaAcc"' 1000000

194 MB total memory in use (0 MB lost due to fragmentation) за Total time 0.77s ( 0.78s elapsed)

Забавно, что вариант с zipWith при таком же аргументе теперь

146 MB total memory in use (0 MB lost due to fragmentation) за Total time 0.64s ( 0.65s elapsed)

Т.е. получше немного, чем со scanl/scanl1.

HolyBoy
() автор топика
Последнее исправление: HolyBoy (всего исправлений: 2)
Ответ на: комментарий от HolyBoy

и близко не воспроизводится,

fermaPyr' :: Int -> Int
fermaPyr' n = (scanl1 (+!) $ scanl1 (+!) [0..]) !! n

(+!) :: Int -> Int -> Int
a +! b = a `seq` b `seq` a + b

fermaPyr'' :: Int -> Int
fermaPyr'' n = p2 !! (n+1)
  where
    p1 = 0:zipWith (+!) p1 [1..]
    p2 = 0:zipWith (+!) p2 p1

main = print . fermaPyr'' . read . head =<< getArgs
qnikst@thinkpad ~/tmp/lor/pyr $ \time -v ./1 100000
166671666700000
	Command being timed: "./1 100000"
	User time (seconds): 0.05
	System time (seconds): 0.00
	Percent of CPU this job got: 96%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.05
	Average shared text size (kbytes): 0
	Average unshared data size (kbytes): 0
	Average stack size (kbytes): 0
	Average total size (kbytes): 0
	Maximum resident set size (kbytes): 62880
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 0
	Minor (reclaiming a frame) page faults: 4037
	Voluntary context switches: 2
	Involuntary context switches: 1
	Swaps: 0
	File system inputs: 0
	File system outputs: 0
	Socket messages sent: 0
	Socket messages received: 0
	Signals delivered: 0
	Page size (bytes): 4096
	Exit status: 0

я даже не знаю что сказать.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Так ведь аргумент на порядок отличается. Если 10^5, как у тебя, то

fermaPyr":

14 MB total memory in use (0 MB lost due to fragmentation), Total time 0.06s ( 0.06s elapsed)

fermaPyr':

18 MB total memory in use (0 MB lost due to fragmentation), Total time 0.07s ( 0.07s elapsed)

Но здесь разница заметна не очень, при аргументе 10^6 получается, как писал выше,

fermaPyr':

194 MB total memory in use (0 MB lost due to fragmentation), Total time 0.76s ( 0.77s elapsed)

fermaPyr":

146 MB total memory in use (0 MB lost due to fragmentation), Total time 0.59s ( 0.59s elapsed)

При этом, конечно, приходится увеличивать размер выделяемой памяти под кучу.

Компиляется с -O2 -rtsopts. Запуск с +RTS -s -K100m

HolyBoy
() автор топика
Последнее исправление: HolyBoy (всего исправлений: 1)
Ответ на: комментарий от qnikst

так ладно, разобрался, так лучше?

qnikst@thinkpad ~/tmp/lor/pyr $ ./1 1000000 +RTS -sstderr
166666666667500000
          66,984 bytes allocated in the heap
           3,512 bytes copied during GC
          44,416 bytes maximum residency (1 sample(s))
          17,024 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0         0 colls,     0 par    0.00s    0.00s     0.0000s    0.0000s
  Gen  1         1 colls,     0 par    0.00s    0.00s     0.0001s    0.0001s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.00s  (  0.00s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.00s  (  0.00s elapsed)

  %GC     time       7.2%  (26.6% elapsed)

  Alloc rate    841,010,960 bytes per MUT second

  Productivity  86.8% of total user, 318.7% of total elapsed
qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Но как? :)

dev-lang/ghc-7.6.3-r1 doc gmp llvm ELIBC=«glibc»

Код у нас один и тот же. Разве только какие-то секретные дефолтные опции для ghc у тебя включены.

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

а не там ошибка, в общем через вектор unboxed сделал, может вечером подправлю и выложу, а так работать надо

qnikst ★★★★★
()
Ответ на: комментарий от HolyBoy

код:

{-# LANGUAGE LambdaCase #-}
import Criterion.Main
import Data.Vector.Unboxed as V
import System.Environment

magic :: Int -> Int
magic n = V.sum p1 
  where
    p1 = V.unfoldrN (n+1) (\(c,s) -> let k = c+s in k `seq` Just (k,(c+1,k))) (0,0) 

-- tail recursion version
-- 
fermaPyr' n = let fAcc 0 a = a
                  fAcc n a = fAcc (n - 1) (fermaTria' n + a)
              in fAcc n 0
    
-- tail recursion ver.2
--
fermaPyr'' :: Int -> Int
fermaPyr'' 0 = 0
fermaPyr'' n = fermaTria' n + fermaPyr'' (n - 1)


-- recursion version
-- 
fermaTria 0 = 0
fermaTria n = n + fermaTria (n - 1)

-- tail recursion version
fermaTria' n = let fAcc 0 a = a
                   fAcc n a = fAcc (n - 1) (n + a)
               in fAcc n 0

main = getArgs >>= \case
         ("mem":x:_) -> membench (read x)
         _           -> criterion
  where 
    membench x = print $ magic x
    criterion  = defaultMain
      [ bench "magic"      $ nf magic      10000 
      , bench "fermaPyr''" $ nf fermaPyr'' 10000
      ]

сборка:

$ ghc -O2 -fllvm -optc-O2 -optc-mtune=native -optc-march=native 1.hs -fforce-recomp
[1 of 1] Compiling Main             ( 1.hs, 1.o )
You are using a new version of LLVM that hasn't been tested yet!
We will try though...
Linking 1 ...

результат (скорость + память):

 \time -v ./1 mem 1000000
166667166667000000
	Command being timed: "./1 mem 1000000"
	User time (seconds): 0.00
	System time (seconds): 0.00
	Percent of CPU this job got: 0%
	Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.00
	Average shared text size (kbytes): 0
	Average unshared data size (kbytes): 0
	Average stack size (kbytes): 0
	Average total size (kbytes): 0
	Maximum resident set size (kbytes): 7632
	Average resident set size (kbytes): 0
	Major (requiring I/O) page faults: 0
	Minor (reclaiming a frame) page faults: 546
	Voluntary context switches: 2
	Involuntary context switches: 1
	Swaps: 0
	File system inputs: 0
	File system outputs: 0
	Socket messages sent: 0
	Socket messages received: 0
	Signals delivered: 0
	Page size (bytes): 4096
	Exit status: 0

бенчмарк

qnikst@thinkpad ~/tmp/lor/pyr $ ./1 
warming up
estimating clock resolution...
mean is 2.012293 us (320001 iterations)
found 24393 outliers among 319999 samples (7.6%)
  15929 (5.0%) low severe
  8464 (2.6%) high severe
estimating cost of a clock call...
mean is 51.33679 ns (13 iterations)
found 1 outliers among 13 samples (7.7%)
  1 (7.7%) high severe

benchmarking magic
mean: 29.38982 ns, lb 29.00721 ns, ub 30.33777 ns, ci 0.950
std dev: 3.062961 ns, lb 1.591688 ns, ub 5.057607 ns, ci 0.950
found 9 outliers among 100 samples (9.0%)
  4 (4.0%) high mild
  5 (5.0%) high severe
variance introduced by outliers: 81.033%
variance is severely inflated by outliers

benchmarking fermaPyr''
mean: 119.3138 us, lb 119.1146 us, ub 119.6163 us, ci 0.950
std dev: 1.240734 us, lb 925.3786 ns, ub 1.972768 us, ci 0.950

qnikst@thinkpad ~/tmp/lor/pyr $ ./1 mem 1000000 +RTS -sstderr
166667166667000000
          68,648 bytes allocated in the heap
           3,512 bytes copied during GC
          44,416 bytes maximum residency (1 sample(s))
          17,024 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0         0 colls,     0 par    0.00s    0.00s     0.0000s    0.0000s
  Gen  1         1 colls,     0 par    0.00s    0.00s     0.0002s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.00s  (  0.00s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.00s  (  0.00s elapsed)

  %GC     time      10.0%  (31.6% elapsed)

  Alloc rate    1,105,727,723 bytes per MUT second

  Productivity  84.0% of total user, 266.1% of total elapsed

offtop: можешь для интереса в HCFLAGS прописать "-fllvm -optc-O2 -optc-march=native" надо это проверить кстати и в блог написать, а то у многих хацкель прог не по делу llvm флаг торчит

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 2)
Ответ на: комментарий от qnikst

Проверил.

Измерение производительности:

warming up
estimating clock resolution...
mean is 1.879371 us (320001 iterations)
found 58439 outliers among 319999 samples (18.3%)
  54439 (17.0%) low severe
  4000 (1.3%) high severe
estimating cost of a clock call...
mean is 49.49042 ns (12 iterations)
found 1 outliers among 12 samples (8.3%)
  1 (8.3%) high severe

benchmarking magic
mean: 27.16012 ns, lb 27.14521 ns, ub 27.18228 ns, ci 0.950
std dev: 92.27698 ps, lb 69.72624 ps, ub 143.0506 ps, ci 0.950

benchmarking fermaPyr''
mean: 113.4742 us, lb 113.3555 us, ub 113.7190 us, ci 0.950
std dev: 838.6306 ns, lb 498.7351 ns, ub 1.630455 us, ci 0.950

Потребление памяти:

166667166667000000
          67,976 bytes allocated in the heap
           3,512 bytes copied during GC
          44,416 bytes maximum residency (1 sample(s))
          17,024 bytes maximum slop
               1 MB total memory in use (0 MB lost due to fragmentation)

                                    Tot time (elapsed)  Avg pause  Max pause
  Gen  0         0 colls,     0 par    0.00s    0.00s     0.0000s    0.0000s
  Gen  1         1 colls,     0 par    0.00s    0.00s     0.0002s    0.0002s

  INIT    time    0.00s  (  0.00s elapsed)
  MUT     time    0.00s  (  0.00s elapsed)
  GC      time    0.00s  (  0.00s elapsed)
  EXIT    time    0.00s  (  0.00s elapsed)
  Total   time    0.00s  (  0.00s elapsed)

  %GC     time       8.6%  (26.2% elapsed)

  Alloc rate    965,115,783 bytes per MUT second

  Productivity  84.7% of total user, 258.1% of total elapsed

Без оптимизации, только с -O2

warming up
estimating clock resolution...
mean is 1.784853 us (320001 iterations)
found 2366 outliers among 319999 samples (0.7%)
  1954 (0.6%) high severe
estimating cost of a clock call...
mean is 49.96217 ns (12 iterations)
found 1 outliers among 12 samples (8.3%)
  1 (8.3%) high mild

benchmarking magic
mean: 13.71027 us, lb 13.70652 us, ub 13.71444 us, ci 0.950
std dev: 20.21804 ns, lb 17.71890 ns, ub 22.87030 ns, ci 0.950

benchmarking fermaPyr''
collecting 100 samples, 1 iterations each, in estimated 6.269598 s
mean: 62.82059 ms, lb 62.70756 ms, ub 63.09518 ms, ci 0.950
std dev: 909.8677 us, lb 488.0118 us, ub 1.480532 ms, ci 0.950
found 13 outliers among 100 samples (13.0%)
  4 (4.0%) high mild
  9 (9.0%) high severe
variance introduced by outliers: 7.523%
variance is slightly inflated by outliers

В данном конкретном случае компиляция под LLVM — добро. Не очень понял, что ты имел в виду под

у многих хацкель прог не по делу llvm флаг торчит

? Судя по флагам компиляции, portage правильно определяет для ghc тип процессора, который у меня стоит, так что, не уверен, что -optc-march=native надо включать.

HolyBoy
() автор топика
Последнее исправление: HolyBoy (всего исправлений: 2)
Ответ на: комментарий от HolyBoy

если не пытаться использовать -fnewcodegen (или как-то так), то llvm бекенд добро почти для всех случаев, т.к. генерирует существенно более хороший код, а цена это исключительно время компиляции. Этот момент особенно существеннен если используется «математический» код и всякие числодробилки. Ну и анбокснутые вектора это тоже огромное добро.

Судя по флагам компиляции, portage правильно определяет для ghc тип процессора, который у меня стоит, так что, не уверен, что -optc-march=native надо включать.

ghc/cabal не видит флаги, которые у тебя стоят в CFLAGS/CPPFLAGS [1] так, что установка HCFLAGS может помочь (хотя я сам пока не пробовал оптимизировать это), так же ты можешь собирать весь код с llvm, учитывая агрессивный инлайнинг на общую производительность это может и не повлиять, но может и повлиять, если функции не инлайнятся.

[1] из haskell-cabal.eclass:

	# currently cabal does not respect CFLAGS and LDFLAGS on it's own (bug #333217)
	# so translate LDFLAGS to ghc parameters (without filtering)
qnikst ★★★★★
()
Ответ на: комментарий от qnikst

ghc/cabal не видит флаги, которые у тебя стоят в CFLAGS/CPPFLAGS [1] так, что установка HCFLAGS может помочь

Так он уже определил. Вот, смотрю сейчас на собирающийся пакет и вижу:

-H32m -O -optc-march=corei7 -opta-march=corei7 -optc-Wa,--noexecstack -opta-Wa,--noexecstack -w --make utils/ghc-cabal/Main.hs -o…

Прописал ещё в make.conf HCFLAGS="-fllvm -optc-O2" и вижу, что это тоже добавилось в список параметров.

HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

вот, если разница будет заметная не забудь отписаться. И overlay gentoo-haskell не забывай использовать, без него грустно :)

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Хорошо, напишу. Что касается gentoo-haskell, то я давно уже, как только понял, насколько медленно обновляется в отношении пакетов из cabal основное дерево, сделал вот так:

# cat /etc/portage/package.mask
dev-haskell/*::gentoo
dev-lang/ghc::gentoo
app-admin/haskell-updater::gentoo
# cat /etc/portage/package.keywords
dev-haskell/*::gentoo-haskell ~amd64
dev-lang/ghc ~amd64
HolyBoy
() автор топика
Ответ на: комментарий от HolyBoy

ну маскировать дерево не обязательно было :).

Вообще политика простая - в дерево идут только приложения для конечных пользователей и их зависимости, а все разработческие либы в официальном оверлее.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Я не был уверен, что зависимости из всех пакетов официального дерева совпадают с таковыми из оверлея и не хотел столкнуться с неопределённостью при установке пакетов, имеющихся в обеих деревьях.

HolyBoy
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.