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

[FEATURE REQUEST] Unsafe apply annotation (avoiding caml_call) #1246

Closed
wlitwin opened this issue Mar 4, 2022 · 3 comments
Closed

[FEATURE REQUEST] Unsafe apply annotation (avoiding caml_call) #1246

wlitwin opened this issue Mar 4, 2022 · 3 comments

Comments

@wlitwin
Copy link

wlitwin commented Mar 4, 2022

Is your feature request related to a problem? Please describe.
Js_of_ocaml's implementation of caml_callN is very costly because it calls f.length to check for partial application (I believe?). It seems Js_of_ocaml can properly eliminate these caml_calls when inside the same module/library, but across library boundaries it has issues. For some of my projects I have tight loops and this f.length is causing a lot of performance problems.

Describe the solution you'd like
I'd like some annotation I can add to an application to inform Js_of_ocaml that it should generate a normal application, and not a call to caml_call. E.g.:

let lib_fun1 t =
    (t.intf.fun1 t.impl)[@js_of_ocaml.unsafe_apply]

Right now it would generate the following code:

    function lib_fun1(t){return caml_call1(t[1][2],t[2])}

when I would like:

    function lib_fun1(t){return t[1][2](t[2])}

Describe alternatives you've considered
From what I gather it's difficult to get some of this information due to how Js_of_ocaml works, so this seems like a decent middle ground. I'm open to other solutions.


I'm also willing to put in the work to make this happen, but a small description/introduction to what places I would need to modify would be helpful.

@hhugo
Copy link
Member

hhugo commented Mar 7, 2022

It seems Js_of_ocaml can properly eliminate these caml_calls when inside the same module/library, but across library boundaries it has issues

This is not quite true. Jsoo cannot sometimes produce direct call within the same module or library (1). Also, it's very much able to use direct call cross modules and libraries (2).

  • (1) calling a function produced by a functor application that was not inlined would produce a direct call.

  • (2) the module / library boundary is only relevant to jsoo when compiling with separate compilation, the situation here could be improved with Separate compilation: generate and use cross module information regarding function arity #550. When compiling using whole program compilation (e.g. when using dune build --profile release), theses boundary are mostly irrelevant.

@wlitwin
Copy link
Author

wlitwin commented Mar 7, 2022

I see, I apologize for my naivety. It seems my issue is not library/module boundaries, but indirection. The case I'm trying to optimize is abstracting over two implementations. For example the architecture looks something like this:

flowchart TD
A[Userland Lib] --> B[Backend 1 Common]
A --> C[Backend 2 Common]
B --> D[Backend 1 Web]
B --> E[Backend 1 Desktop]
C --> F[Backend 2 Web]
C --> G[Backend 2 Desktop]
Loading

Originally I was using functors, but that proved to be too heavy, and then I tried first class modules and records of functions, but I can't seem to get rid of the caml_calls in the intermediate libraries. Flambda (for desktop) removed them in almost all the cases, but I'm trying to coax Js_of_ocaml to do the same.

Here's a simplified example of what I was trying to do: https://github.com/wlitwin/caml_call_example In this example the functions in Lib have to go through caml_call when calling the implementations. Maybe there's a better way to go about this?

Js output:

    function lib_create(intf,param){return [0,intf,caml_call1(intf[1],param)]}
    function lib_fun1(t){return caml_call1(t[1][2],t[2])}
    function create(f){return [254,f]}
    function fun1(t){var _bg_=t[1];return caml_call1(printf(_bd_),_bg_)}
    var impl=[0,create,fun1];
    function create$0(f){return [254,f]}
    function fun1$0(t){var _bf_=t[1];return caml_call1(printf(_be_),_bf_)}
    var
     impl$0=[0,create$0,fun1$0],
     ctx1=lib_create(impl,10.),
     ctx2=lib_create(impl$0,20.);
    lib_fun1(ctx1);
    lib_fun1(ctx2);
    do_at_exit(0);
    return}

@hhugo
Copy link
Member

hhugo commented Mar 26, 2024

#1358 should have fixed the perf issue.

@hhugo hhugo closed this as completed Mar 26, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants