[Agda] summing rationals
Sergei Meshveliani
mechvel at botik.ru
Sun Jun 25 13:23:14 CEST 2017
On Sun, 2017-06-25 at 09:46 +0200, Ulf Norell wrote:
> The rationals are not very well-developed in agda-prelude.
> Nevertheless I get unsurprising performance compared to ghc (~2min for
> n = 20,000 on my machine) with the following benchmark:
> [..]
What is the machine that runs your test?
(how much frequency [GHz] has it?).
What timing GHC shows on your machine for n = 200,000 for
import Data.Ratio ((%))
f :: Integer -> Rational -- :: Ratio Integer
f n =
sum [1 % k | k <- [1 .. n]]
> ghc --make -O Test
?
DoCon-A (Agda) has 21 sec for n = 2000, for a 3 GHz personal
computer.
> The reason that DoCon performs so well is that it has a more clever
> implementation of _+_. agda-prelude and GHC implements _+_ as
>
>
> (p :% q) + (p1 :% q1) = (p * q1 + p1 * q) % (q * q1)
> where a % b = let d = gcd a b in div a d :% div d b
>
> DoCon implements _+_ as
>
>
> (p :% q) + (p1 :% q1) = div n dd :% (q' * q1' * div d dd)
> where
> d = gcd q q1
> q' = div q d
> q1' = div q1 d
> n = p * q1' + p1 * q'
> dd = gcd n d
>
>
> I can't claim to understand why this is so much faster, but it is.
>
As I recall, I just copied there the method from the old book
"The art of computer programming" by D.Knuth.
As I understand, the main idea is that for large numbers multiplication
causes the result size growth (number of (macro)digits), while divMod
and gcd do not.
And the sizes of (p * q1), (p1 * q), (q * q1) effect the cost of the
further operations applied to obtain the final fraction.
Hence it is better to cancel p, p1, q, q1 by the corresponding gcd-s as
soon as possible.
But for _random_ numbers a naive method of GHC is, probably, faster on
average than Knuth's. Because for two random numbers m, n, it often
holds gcd m n = 1.
But in Harmonic, and in many real mathematical tasks, fractions are
often accumulated in the middle of loops, they are "less random".
--------------
I do not find, so far, why DoCon-A shows a greater cost order than GHC.
May be this is _-fr_ that probably imply _∸_ ? May be, _∸_ or `pred' is
slow, I do not know.
Thanks for comments,
------
Sergei
> On Sat, Jun 24, 2017 at 9:06 PM, Sergei Meshveliani <mechvel at botik.ru>
> wrote:
> Dear all,
>
> Consider the following performance test for rational
> arithmetic.
> Call this test call "Harmonic":
>
> f n = sum [1/k | k <- [1 .. n]] for n : Nat, 1/k :
> Rational,
>
> sum = foldr _+_ 0 where _+_ = fraction addition
>
> test n = let fn = f n
> in fn - fn
>
> Read from file n = 100, 1000, 2000, 100.000 ...,
>
> print (test n) and check time.
>
> (fn - fn) is applied here in order to avoid printing large
> values.
>
>
> --------------------------- Haskell code -------
> import Data.Ratio ((%))
>
> f :: Integer -> Rational -- :: Ratio Integer
> f n =
> sum [1 % k | k <- [1 .. n]]
> -------------------------------------------------
>
>
> I compare the three libraries:
>
> (GHC) GHC (Glasgow Haskell 7.10.2),
>
> (D) DoCon-2.12 written in Haskell, running under GHC,
>
> (DA) DoCon-A 2.00-pre written in Agda,
> running under Agda 2.5.2 -like.
>
> (D) and (DA) keep fractions with numerator and denominator
> mutually
> coprime, and cancel fractions most eagerly by gcd.
>
> (GHC) does not cancel by gcd so eagerly.
>
> The performance comparison on Harmonic is:
>
> (D) >> (GHC) > (DA).
>
> (D) takes n = 200.000 easily, while GHC "hangs" on 20.000
> -- for a 3 GHz machine.
>
> (GHC) is considerably faster (DA)
> (DA hangs for long on 4000).
>
> The method in (DA) is the same as (D).
> gcd is almost as fast as GHC.gcd, this has been tested.
> Division (with remainder) is via dimMod of Agda-prelude, it
> is fast.
>
> And I wonder why (DA) is so slow.
> (DA) carries all the needed proofs (for coprimality, and
> such).
> But I hope, this does not effect performance any essentially.
>
> Can anybody provide the code for testing Harmonic for
> Rational numbers
> in agda-prelude ?
>
> The (DA) test for (Fraction Integer) is like this:
>
> ------------------------------------------------------------------------
> ...
> open Fraction.F0 Int1.integralRing-with∣? Int1.gcdRing using
> (Fraction; fraction;
> fraction-showInst)
> open Fraction.F1 Int1.integralRing-with∣? Int1.gcdRing using
> (frRingoid)
> open Ringoid frRingoid using (_-_)
> open Fraction
>
> inv-n : ℕ → Fraction -- \n → 1/(1+n)
> inv-n n =
> fraction (+ 1) (+ (suc n)) +suc≢+0
>
> f : ℕ → Fraction -- sum [1/n | n <- [1 .. n+1]]
> f n =
> sum frRingoid (map inv-n (natSegment 0 n))
>
> main : IO Unit
> main = (readFiniteFile "data.txt") >>= putStrLn ∘ toCostring ∘
> g
> where
> g : String -> String
> g str =
> -- n is first read from file data.txt
>
> concatStr
> ("n = " ∷ showNat n ∷
> ", " ∷
> "num (f n - f n) = " ∷ showInt (num (fn - fn)) ∷ "\n
> \n"
> ∷ []
> )
> where
> n = stringToNat $ filterStr decimalDigit? str
> fn = f n
> -------------------------------------------------------------------------
>
>
> So far, I do not find the performance bottleneck in (DA).
>
> Thanks,
>
> ------
> Sergei
>
>
>
> _______________________________________________
> Agda mailing list
> Agda at lists.chalmers.se
> https://lists.chalmers.se/mailman/listinfo/agda
>
>
More information about the Agda
mailing list