It’s hard to be sure without context, but my guess would be that
maybe_timeout is inlined at the callsite, and that the compiler is able to unbox the float value that you pass as
timeout argument. It remains unboxed in the body of the function, until the external function
Lwt_unix.with_timeout is called, as it expects this argument boxed – boxing then allocates at each call.
In the second, hard-coded version,
5.0 is a shared constant (as a boxed float), so no allocation occurs due to boxing.
You might be able to verify that claim by using an
[@@inline never] attribute on the
maybe_timeout declaration, which should enforce boxing at its callsite and make the performance of both versions consistent (they allocate as much). I don’t remember whether only flambda honors this attribute, or also the non-flambda middle-end. (And I don’t know whether you use flambda or not.)
Another way to verify the hypothesis would be to force boxing of the value passed to
timeout before the call. I guess that, for example, calling
print_float on that value (before passing it to
timeout) could work.