On Sun, 2021-03-21 at 00:43 +0000, Christopher Baines wrote: > This isn't meant to change the way errors are handled, and arguably makes the > code harder to read, but it's a uninformed attempt to improve the > performance (following on from a performance regression in > 205833b72c5517915a47a50dbe28e7024dc74e57). > > I'm guessing something about Guile internals makes calling (loop ...) within > the catch bit less performant than avoiding this and calling (loop ...) after > the catch bit has finished. Since this happens lots, this seems to be > sufficient to make guix weather a lot slower than it was before. I took a look at the code, and it seems we did somthing like: (let loop VARS (match sent NON-LOOPING-CASES (STUFF (catch #t THUNK-THAT-MIGHT-CALL-LOOP-IN-TAIL-POSITION SOME-HANDLER-THAT-DOES-NOT-CALL-LOOP) A small demonstration of what could go wrong with such a construction (run this in the Guile REPL): (let loop ((attempts-todo 20)) (catch 'oops (lambda () (if (<= 0 attempts-todo) (loop (- attempts-todo 1)) (throw 'oops))) (lambda _ (display 'too-bad!) (newline) (backtrace)))) Output: too-bad! Backtrace: In ice-9/boot-9.scm: 1731:15 19 (with-exception-handler # _ # _ …) [The previous line repeated 17 times] 1731:15 1 (with-exception-handler # _ # _ …) In unknown file: 0 (backtrace #) With this construction, we were nesting exception handlers within exception handlers ... So in hindsight it doesn't seem surprising this isn't very performant. (THUNK-THAT-MIGHT-CALL-LOOP-IN-TAIL-POSITION itself is not called in the tail-position of the '(let loop ...)' form.) Hope that sheds some light on the matter, Maxime