On Sep 18, 2017, at 3:07 PM, Adam adam.mlmb@gmail.com wrote:
Hi Marc,
Thank you very much for clarifying.
Three brief followup questions at the bottom (marked 1. 2. 3.).
2017-09-18 23:55 GMT+08:00 Marc Feeley feeley@iro.umontreal.ca: [..]
Also.. if I understand you right, there are cases when Gambit with auto-forcing enabled also would fail evaluating (abs (delay 0)) .
With --enable-auto-forcing, this case works only when abs is not inlined, in other words an actual function call to abs is performed (because the library definition correctly forces the argument). This can be achieved in various ways, like (declare (not run-time-bindings abs)) or (declare (standard-bindings) (not inline-primitives abs)).
This needs to be fixed so that those declarations, which typically improve execution speed, can be used reliably in a system built with --enable-auto-forcing.
Ahaa, so that is a limit that exists currently for --enable-auto-forcing . Thanks for pointing out!
[..] Also, when using the default (declare (safe)) the compiler converts (car lst) into
(if (##pair? lst) (##car lst) (car lst))
rather than the correct
(let ((lst (##force lst))) (if (##pair? lst) (##car lst) (car lst)))
This was just another iteration of what you said already in the section above right?
I guess…
[..]
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.
Actually this is not quite true… f will be forced in the call (f x)
[also in compiled mode - 3. I interpret you to mean that here, that is correct, right?]
Yes in compiled mode.
thanks to this definition in lib/_kernel.scm:
(define-prim (##apply-with-procedure-check oper args) (##declare (not interrupts-enabled)) (macro-force-vars (oper) (if (##procedure? oper) (##apply oper args) (##raise-nonprocedure-operator-exception oper args #f #f))))
Wait, what does |##apply-with-procedure-check| actually do, in what situations is it invoked, is this run on all (f a1 a2 ...) with oper = f and args = (list a1 a2 ...) for any procedure call made anywhere, when compiling with (declare (safe))?
No… In the scope of a (declare (safe)) the generated C code will check if the “operator” position, the f here, is a procedure. A direct transfer of control to f is done when f is a procedure. The function ##apply-with-procedure-check is tail called by the runtime system when (##procedure? oper) is #f. Note that it could be that oper is a promise whose forced value is a procedure, so ##apply-with-procedure-check forces oper and checks if the forced value is a procedure (assuming the runtime system was compiled with --enable-auto-forcing)
- So if I just remove the |macro-force-vars| in there, |f| will not be forced in compiled mode?
Yes.
- Just if it is possible, is there some easy way to make also |f| *not* be forced in compiled mode?
Just don’t use --enable-auto-forcing… or use (declare (not safe)) so that the runtime system doesn’t check that f is a procedure. Note that with (declare (safe)) in the case of the operator position of a call the auto-forcing doesn’t add any overhead because the common case is that the operator position is a procedure.
Not forcing |f| ever, would be useful in situations where you use the auto-forcing only to force data structures but never any code.
I hypothesize that this will provide significant speed increases.
No… see previous comment. There is zero cost for auto-forcing the operator position in safe mode.
Will test and benchmark following your next clarification.
Thanks a lot!
I don’t understand why you are so concerned with this issue (forcing the operator position of a call)… The real overhead is auto-forcing data-structures… A good approach to minimize the overhead is a dataflow analysis or even BBV…
Marc