Dear Marc,

Thank you very much for your clarification. Followup question below, to really understand what you are saying.

2017-08-15 21:53 GMT+08:00 Marc Feeley <feeley@iro.umontreal.ca>:

> On Jul 27, 2017, at 6:45 AM, Adam <adam.mlmb@gmail.com> wrote:
>
> I'll make closer benchmarking of auto forcing later, but, it incurs a quite steep overhead.

Experimental data please!

I will provide it.
  
> Meanwhile, I have a question:
>
>
> With --enable-auto-forcing, at procedure calls, that is (procedure 'arg1 'arg2 'etc.), |procedure| is forced, as we can see by the example that in both interpreted and compiled mode, ((delay (begin (println "Forced.") (lambda (v) v))) #t) evaluates (and it prints out "Forced.").
>
>
> I'd wildly guess that quite a lot of the overhead in auto-forcing is here. If you're sure the operator will never be a promise in any procedure call, then this particular aspect of auto forcing could be disabled.
>
> (Without checking, I would guess that while Gambit may optimize so that local procedure calls may avoid being brought through the |force| logics, then, this optimization would do so that calls to procedures defined in *other* modules such as the runtime, would *not* be taken through |force| also. That should be positive for speed.)
>
>
> Would it be possible for me to disable this particular aspect of the auto-forcing, to get higher performance?

Currently auto-forcing only works in interpreted code.  So if your program does (f x) and (car y) and you compile that, then “f” will not be forced and “y” will not be forced if car is inlined, for example if you (declare (not safe)).  You can consider this a bug… to be fixed.

However, compiled predefined functions do the auto-forcing (when the system is configured with --enable-auto-forcing).  So (car y) will force y if the actual function is called, for example if you (declare (safe)), which is the default.

Note that for “safe” compiled code, when compiling (f x) where f is a mutable global variable, it is necessary to generate a check that f is a procedure.  If it is not a procedure a handler is called that normally raises an exception.  This handler could easily be extended to first force the value and check if the resulting value is a procedure and proceed with the call if it is (hooray for raising exceptions in tail-position!).  So there would be no additional cost for safe compiled code.

Can you please clarify what you mean here, by giving one or two pieces of example code, that illustrate the difference between various code operation options - so.. that would be

1) Interpreted code, vs.
2) Compiled code with (declare (safe)), vs.
3) Compiled code with (declare (not safe))

(I guess maybe (declare (block)) vs. (declare (separate)) could affect forcing behavior, as inlined code not would be forced, but non-inlined code could.)



In either case if I understand you right, there are examples where Gambit with auto-forcing enabled, will fail executing ((delay (lambda () 'hello-world))) .

Also.. if I understand you right, there are cases when Gambit with auto-forcing enabled also would fail evaluating (abs (delay 0)) .

Only for my clarity, please tell in what interval of conditions these will fail.


Thanks!