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

Closure computation is quadratic in the number of functions #7826

Open
vicuna opened this Issue Jul 19, 2018 · 2 comments

Comments

Projects
None yet
1 participant
@vicuna
Copy link
Collaborator

vicuna commented Jul 19, 2018

Original bug ID: 7826
Reporter: ppedrot
Status: acknowledged (set by @xavierleroy on 2018-07-19T16:01:07Z)
Resolution: open
Priority: normal
Severity: minor
Version: 4.06.1
Category: middle end (typedtree to clambda)
Monitored by: @nojb

Bug description

The Closure.close_functions function computes the set of free variables of its arguments, which is O(n). Unluckily this function is recursive and it subcalls can do the same, which leads to a runtime quadratic in the number of nested abstraction nodes.

I doubt this is an issue in handwritten code, but the problem sure happens in generated code. I hit it in the native compilation scheme of Coq code from fiat-crypto, and the compiler takes ages for a term that is big but not crazy either (~ 500kloc of sparse code, every line being a constructor most of the time).

I do not have a small reproducible test-case at hand, this would require extracting it from the generated code and making it standalone, which is not easy.

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented Jul 19, 2018

Comment author: ppedrot

Easy enough to reproduce with a trivial example:

gen.ml:
%%%%%%%%%%%%%%%%%%

let file = "out.ml"

let clos_size = int_of_string Sys.argv.(1)

let rec gen chan n =
if n = 0 then ()
else
let () = Printf.fprintf chan "fun x_%i ->\nlet () = print_newline () in\n" n in
gen chan (n - 1)

let () =
let chan = open_out file in
let () = Printf.fprintf chan "let f =\n%!" in
let () = Printf.fprintf chan "%a\n%!" gen clos_size in
let () = Printf.fprintf chan "()\n%!" in
close_out chan

%%%%%%%%%%%%%%%%%%

$ ocaml gen.ml 5000 && time ocamlopt -c out.ml
2.45user 0.11system 0:02.56elapsed 99%CPU (0avgtext+0avgdata 86292maxresident)k

$ ocaml gen.ml 10000 && time ocamlopt -c out.ml
10.11user 0.22system 0:10.33elapsed 99%CPU (0avgtext+0avgdata 163768maxresident)k

$ ocaml gen.ml 20000 && time ocamlopt -c out.ml
30.02user 0.45system 0:30.48elapsed 99%CPU (0avgtext+0avgdata 319000maxresident)k

@vicuna

This comment has been minimized.

Copy link
Collaborator Author

vicuna commented Jul 19, 2018

Comment author: @xavierleroy

Yes, this is one of the many quadratic behaviors in ocamlopt. (There are exponential behaviors too.) They are never encountered by hand-written code and sometimes encountered by auto-generated code. As a consequence, nobody feels a strong urge to rewrite from scratch with more efficient algorithms when they exist.

@vicuna vicuna added the middle-end label Mar 14, 2019

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.