agda-stdlib icon indicating copy to clipboard operation
agda-stdlib copied to clipboard

Data.List.upTo/applyUpTo can be made faster

Open Taneb opened this issue 1 year ago • 4 comments

In a quick experiment, the following definition runs circles around the current implementation of upTo

goUpTo : ℕ → ℕ → List ℕ
goUpTo _ 0 = []
goUpTo n (suc i) = n ∷ goUpTo (suc n) i

upTo′ : ℕ → List ℕ
upTo′ = goUpTo 0

I suspect this is because applyUpTo builds huge closures.

Taneb avatar Jul 11 '24 05:07 Taneb

(Deleted my original comment, but now reinstating it) Can the implementation be further improved/speeded-up by considering a tail-call optimisation?

jamesmckinna avatar Jul 11 '24 13:07 jamesmckinna

Can the implementation be further improved/speeded-up by considering a tail-call optimisation?

I don't think so. The recursion here is already guarded behind a constructor. I don't know what a tail recursive implementation would look like.

Taneb avatar Jul 11 '24 17:07 Taneb

Yes, I never had performance in mind when I add any of my definitions. Apologies! There's probably plenty more of these lurking in the library...

MatthewDaggitt avatar Jul 17 '24 11:07 MatthewDaggitt

So...: instead have

upTo : ℕ → List ℕ
upTo = go 0
  module UpTo where
    go : ℕ → ℕ → List ℕ
    go _ zero    = []
    go n (suc i) = n ∷ go (suc n) i

downFrom : ℕ → List ℕ
downFrom zero    = []
downFrom (suc n) = n ∷ downFrom n

to avoid building the closures in applyUpTo/applyDownFrom? or is it easier simply to compute downFrom, and then compute upTo = reverse ∘ downFrom?

allFin seems trickier, because the closures, even if only specialised to powers of suc, do seem necessary? (although the reverse trick would again avoid such unpleasantness? UPDATED: er, no?)

Not clear what the knock-on effects on Data.List.Properties might be, but ...

jamesmckinna avatar Jan 20 '25 09:01 jamesmckinna