[Agda] timing for agda-prelude gcd
Sergei Meshveliani
mechvel at botik.ru
Wed Jun 21 22:20:30 CEST 2017
Dear all,
Earlier there was discussed in this list the performance of _+_ and _*_
on Nat. I provided a test program.
And as I recall, the performance is high: as in the Haskell program for
ghc-7.10.2.
Now I need fast divMod and gcd for Nat.
Standard library is very-very slow at this point.
So I try agda-prelude.
It shows considerably faster.
But it is still slower than GHC.
gcd is about 12 times slower,
and divMod is about 4000 times slower (?).
I wonder: what makes this difference with the performance of _+_ and
_*_ ?
(mainly for divMod).
On practice, I need to find d = gcd m n and then to divide m and n by
d. And both need to be fast.
gcd looks reasonable (if I do not mistake),
but division is via dimMod, it its slows down everything.
The code (for Agda-2.5.2 -like and for Haskell) is attached in
GCDTest.agda
It is small.
`main' of Agda reads n from file.
`main' of Haskell sets n in the program (I hope it would not compute
much at compile-time -- ?).
As the loops are long, I hope this does not make a real difference in
time.
The Haskell code is `made' by
ghc -O --make
The Agda code includes auxiliary functions that read n and convert to
Nat (may be, Standard needs to provide such functions?).
After the Agda code, there follows the timing table,
then it follows the Haskell code.
There is produced a long list rs of results for divMod (for gcd),
and it is printed (maximum rs).
`maximum' is applied in order to avoid printing large values.
Can people, please, try this code and tell, may be I am missing
something?
The thing needs consideration and verification.
Thanks,
------
Sergei
-------------- next part --------------
module GCDTest where
open import Foreign.Haskell
open import IO.Primitive
open import Data.String as Str using (String; toCostring; _++_)
open import Function using (_$_; _∘_; flip; case_of_)
open import Relation.Nullary using (yes; no)
open import Relation.Nullary.Decidable using (⌊_⌋)
open import Relation.Unary using (Decidable)
open import Relation.Binary.PropositionalEquality using (_≢_)
open import Data.Product using (_×_; _,_; _,′_; proj₁; proj₂)
open import Data.Char as Char using (Char)
open import Data.List using (List; []; _∷_; map; foldr; reverse; filter)
import Data.Fin as Fin
open import Data.Nat using (ℕ; suc; pred; _+_; _*_; _∸_; _≤_; _≤?_)
open import Data.Nat.Show using () renaming (show to showNat)
-- of UNPrelude --
open import Prelude.String
open import Prelude.Nat
open import Numeric.Nat.DivMod using (rem) renaming (divMod to uDivMod)
open import Numeric.Nat.GCD using (gcd!)
--****************************************************************************
concatStr : List String → String
concatStr = foldr _++_ ""
filterStr : {P : Char → Set} → Decidable P → String → String
filterStr P? = Str.fromList ∘ filter (⌊_⌋ ∘ P?) ∘ Str.toList
isDecimalDigit : Char → Set
isDecimalDigit c = 48 ≤ n × n ≤ 57 where
n = Char.toNat c
decimalDigit? : Decidable isDecimalDigit
decimalDigit? c =
let n = Char.toNat c
in
case ((48 ≤? n) ,′ (n ≤? 57)) of \
{ (yes le , yes le') → yes (le , le')
; (no 48≰n , _ ) → no (48≰n ∘ proj₁)
; (_ , no n≰57) → no (n≰57 ∘ proj₂)
}
charToDecDigitNat : Char -> ℕ
charToDecDigitNat c = (Char.toNat c) ∸ 48
readNat : List Char -> ℕ -- convert from decimal digit chars
readNat = toN ∘ map charToDecDigitNat ∘ reverse
where
toN : List ℕ -> ℕ
toN [] = 0
toN (j ∷ js) = j + (10 * (toN js))
stringToNat : String → ℕ
stringToNat = readNat ∘ Str.toList
max : ℕ → List ℕ → ℕ
max x [] = x
max x (y ∷ xs) = case x ≤? y of \ { (yes _) → max y xs
; (no _) → max x xs }
natSegment : ℕ → ℕ → List ℕ
natSegment m n = -- [k | m ≤ k ≤ n]
segm m (n ∸ m)
where
segm : ℕ → ℕ → List ℕ
segm m 0 = m ∷ []
segm m (suc cnt) = m ∷ (segm (suc m) cnt)
------------------------------------------------------------------
remBySuc : ℕ → ℕ → ℕ
remBySuc m n = rem (uDivMod (suc n) m)
testRem : ℕ → ℕ
testRem n = max 0 (map (remBySuc n) (natSegment 0 (n ∸ 2)))
testGCD : ℕ → ℕ
testGCD n = max 0 (map (gcd! n) (natSegment 1 (pred n)))
main : IO Unit
main = (readFiniteFile "data.txt") >>= putStrLn ∘ toCostring ∘ g
where
-- n is first read from file data.txt
g : String -> String
g str =
concatStr
("n = " ∷ showNat n ∷ ", " ∷
"testRem = " ∷ showNat (testRem n) -- chhose
-- "testGCD n = " ∷ showNat (testGCD n) -- here
∷ []
)
where
n = stringToNat $ filterStr decimalDigit? str
{- ------------------------------------------
Timing for Agda 2.5.2, 3 GHz machine.
n | Time [sec]
|
------- testRem -----------------------------
| |
| Agda 2.5.2 | ghc-7.10.2
---------------------------------------------
20000 | 3 (value = 9999) | 0.000
30000 | 9 (value = 14999) | 0.004
40000 | 17 (value = 19999) | 0.004
| |
500000 | | 0.3
------- testGCD ---------------------
|
| Agda 2.5.2 | ghc-7.10.2
-------------------------------------
500000 | 0.8 |
1000000 | 1.7 |
5000000 | 10.7 | 0.8
20000000 | | 3.1
-}
-- Haskell code ******************************************************
max' :: Integer -> [Integer] -> Integer -- maximum (x: xs)
max' x [] = x
max' x (y: xs) = if x < y then max' y xs
else max' x xs
modTest :: Integer -> Integer
modTest n =
max' 0 (map (\ k -> mod n (succ k))
([0 .. (n - 2)] :: [Integer])
)
gcdTest :: Integer -> Integer
gcdTest n =
max' 0 (map (gcd n) [1 .. (pred n)])
n = 20000000 :: Integer -- edit this
main :: IO ()
main = putStrLn (show (modTest n))
-- gcdTest edit this
More information about the Agda
mailing list