Issue #16663 has been updated by Eregon (Benoit Daloze).


jeremyevans0 (Jeremy Evans) wrote in #note-8:
> I disagree.  The usage is not limited to find only one caller.  You can use it to find multiple callers, such as the first two callers meeting a criteria.  I think limiting it to a single caller would make a common case easier, but would make other cases more difficult.

It's not really limiting, the block can do anything, including getting two callers and then returning the result, or the user can even use `break`/`return` if they find that easier.
But it makes it possible for common cases to do something really efficient.

> If I understand correctly, one of the reasons @Eregon is proposing `find_caller` over `each_caller` is optimization.  If that is correct, I think we should only go with `find_caller` if we have benchmarked it and it has a significant performance advantage over `each_caller`.

That's correct.

> Without benchmarks, I'm not convinced that avoiding the `return`/`break` will make a significant performance difference, at least on CRuby.

We should benchmark it.
It might not make a difference on current CRuby, but I suspect it does on Ruby implementations with a JIT or with frames which can actually skip being allocated, or for VMs which can't use longjmp() and instead must use some kind of exceptions for non-local returns (the case on JVM).



----------------------------------------
Feature #16663: Add block or filtered forms of Kernel#caller to allow early bail-out
https://bugs.ruby-lang.org/issues/16663#change-94788

* Author: headius (Charles Nutter)
* Status: Open
* Priority: Normal
----------------------------------------
There are many libraries that use `caller` or `caller_locations` to gather stack information for logging or instrumentation. These methods generate an array of informational stack frames based on the current call stack.

Both methods accept parameters for `level` (skip some number of Ruby frames) and `length` (only return this many frames). However many use cases are unable to provide one or both of these.

Instrumentation uses, for example, may need to skip an unknown number of frames at the top of the trace, such as to dig out of rspec plumbing or active_record internals and report the first line of user code. In such cases, the typical pattern is to simply request *all* frames and then filter out the one that is desired.

This leads to a great deal of wasted work gathering those frames and constructing objects to carry them to the user. On optimizing runtimes like JRuby and TruffleRuby, it can have a tremendous impact on performance, since each frame has a much higher cost than on CRuby.

I propose that we need a new form of `caller` that takes a block for processing each element.

```ruby
def find_matching_frame(regex)
  caller do |frame|
    return frame if frame.file =~ regex
  end
end
```

An alternative API would be to allow passing a query object as a keyword argument, avoiding the block dispatch by performing the match internally:

```ruby
def find_matching_frame(regex)
  caller(file: regex)
end
```

This API would provide a middle ground between explicitly specifying a maximum number of stack frames and asking for all frames. Most common, hot-path uses of `caller` could be replaced by these forms, reducing overhead on all Ruby implementations and drastically reducing it where stack traces are expensive.



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>