Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"greedy traversal" test performance #146

Closed
Porges opened this issue Nov 5, 2017 · 4 comments
Closed

"greedy traversal" test performance #146

Porges opened this issue Nov 5, 2017 · 4 comments

Comments

@Porges
Copy link
Contributor

Porges commented Nov 5, 2017

This is the test in question:

// FIXME This test takes quite some time to run, it would be good to profile
// FIXME this and find out where the hotspots are. I have a much more complex
// FIXME version of the same test in Haskell and it finishes in a few seconds,
// FIXME even in GHCi (the interpreter).
[<Fact>]
let ``greedy traversal with a predicate yields the perfect minimal shrink``() =
Property.check <| property {
let! xs = Gen.mapTree Tree.duplicate genExp |> Gen.resize 20
match tryFindSmallest noAppLit10 xs with
| None ->
return true
| Some (App (Lit 0, Lit 10)) ->
return true
| Some x ->
return! property {
counterexample ""
counterexample "Greedy traversal with predicate did not yield the minimal shrink."
counterexample ""
counterexample "=== Minimal ==="
counterexample (sprintf "%A" (App (Lit 0, Lit 10)))
counterexample "=== Actual ==="
counterexample (sprintf "%A" x)
return false
}
}

I ran this under the profiler and the problem is entirely due to the sprintf in forAll:

let forAll (gen : Gen<'a>) (k : 'a -> Property<'b>) : Property<'b> =
let handle (e : exn) =
Gen.constant (Journal.singleton (string e), Failure) |> ofGen
let prepend (x : 'a) =
bind (counterexample (sprintf "%A" x)) (fun _ -> try k x with e -> handle e) |> toGen
Gen.bind gen prepend |> ofGen

Replacing this with x.ToString() reduces the runtime from to 40 s to 0.55 s (!)

However, ToString isn't as useful as sprintf "%A" for 'native' F# types. A better fix would be to make this function lazily-invoked, so that it's only called if needed.

@moodmosaic
Copy link
Member

Very interesting. See also #135.

@Porges
Copy link
Contributor Author

Porges commented Nov 6, 2017

@moodmosaic yes, I suspect there are a bunch more easy fixes like this 🙂

@moodmosaic
Copy link
Member

Fixed in #147 and released as Hedgehog 0.6.

@cmeeren
Copy link
Contributor

cmeeren commented Nov 7, 2017

Just want to let you guys know that this reduced my test suite for a small-ish project (156 tests) from 14 seconds to 4 seconds. Great job, keep it up! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants