Issue #3917 has been updated by ko1 (Koichi Sasada). Status changed from Feedback to Closed I close this ticket. Let's discuss naming issue with https://bugs.ruby-lang.org/issues/7051 ---------------------------------------- Feature #3917: [proposal] called_from() which is much faster than caller() https://bugs.ruby-lang.org/issues/3917#change-31660 Author: kwatch (makoto kuwata) Status: Closed Priority: Normal Assignee: ko1 (Koichi Sasada) Category: core Target version: 2.0.0 =begin I propose to introduce Kernel#called_from() which is similar to caller() but much faster than it. Background ---------- There are some cases to want to know from where current method is called. In this case, Kernel#caller() is used. But Kernel#caller() has performance issues for these cases. * caller() retrieves entire stack frame. It is too heavy. * caller() returns an array of "filename:linenum in `method'" string. User must parse it and retrieve filename and linenum by rexp. It is also very heavy weight task. Therefore I propose Kernel#called_from() which is very light weight compared to caller(). A certain benchmark shows that called_from() is more than 20 times faster tan caller(). ??¨ã?®ã?¡ã?½ã??????????©ã??????????¼ã?³å?ºã????????????????¥ã??????????´å???????¨ã????©ã??????????? ??????????????´å????¯é??å¸? Kernel#caller() ???使ã?????????? ????????? Kernel#caller() ??¯ã?????????????£ã????¨é????§ã?¯ã???????©ã?¼ã????³ã?¹ã?????常ã?«æ?ªã????? * caller() ??¯ã?¹ã?¿ã????¯ã????¬ã?¼ã??????????¹ã?¦å???????ºã?????????????¯é??常ã?«é????????ä½???? * caller() ??? "?????¡ã?¤ã?«å??:è¡???ªå? in `??¡ã?½ã????????'" ??¨ã????????å??????®é????????è¿??????? ??¦ã?¼ã?¶ã?¯æ£è¦¨ç??使ã?£ã?¦ã????®æ??å??????????????????????¼ã?¹ã????ªã???????°ã?ªã????ªã????? ??????????????????ä½???? ?????®ã????????Kernel#called_from() ???追å?????????????¨ã?????æ¡?????????? ?????®ã?¡ã?½ã???????? caller() ??¨æ????¹ã?¦é??常ã?«å??ä½????軽ã?????????³ã???????¼ã?¯ã?§ã?? called_from() ??? caller() ??¨æ????¹ã??20???以ä??é«??????? Spec ----- call-seq: called_from(start=1) -> array or nil Returns file name, line number, and method name of the stack. The optional _start_ parameter represents the number of stack entries to skip. Returns +nil+ if _start_ is greater than the size of current execution stack. Raises ArgumentError if _start_ is negative value. Example code ------------ # example.rb 1: def f1() 2: f2() 3: end 4: def f2() 5: f3() 6: end 7: def f3() 8: p called_from() #=> ["example.rb", 5, "f2"] 9: p called_from(0) #=> ["example.rb", 9, "f3"] 10: p called_from(1) #=> ["example.rb", 5, "f2"] 11: p called_from(2) #=> ["example.rb", 2, "f1"] 12: p called_from(3) #=> ["example.rb", 15, "<main>"] 13: p called_from(4) #=> nil 14: end 15: f1() Use Case -------- Case 1: logging method def log_info(message) filename, linenum, _ = called_from() # !!! @logger.info "#{filename}:#{linenum}: #{message}" end Case 2: debug print def debug(message) filename, linenum, _ = called_from() # !!! $stderr.puts "*** DEBUG: #{filename}:#{linenum}: #{message}" end Case 3: deprecation message def send(*args) filename, linenum, _ = called_from() # !!! msg = "`send()' is deprecated. use `__send__()' instead." msg << " (file: #{filename}, line: #{linenum})" $stderr.puts "*** warning: #{msg}" __send__(*args) end Case 4: ActiveSupport::Testing::Pending module ActiveSupport::Testing::Peding def pending(description = "", &block) : #caller[0] =~ (/(.*):(.*):in `(.*)'/) # original #@@pending_cases << "#{$3} at #{$1}, line #{$2}" # original #print "P" # original filenemae, linenum, method = called_from() # !!! @@pending_cases << "#{method} at #{filename}, line #{linenum}" print "P" : end end Case 5: activesupport/lib/active_support/core_ext/module/delegation.rb class Module def delegate(*methods) : #file, line = caller.first.split(':', 2) # original #line = line.to_i # original file, line, _ = called_from() # !!! : module_eval(<<-EOS, file, line - 5) : end end Case 6: caching helper for template system def cache_with(key) data, created_at = @_cache_store.get(key) filename, = called_from() # !!! ## if template file is newer than cached data then clear cache. ## (performance is very important in this case.) if created_at < File.mtime(filename) data = nil @_cache_store.del(key) end ## if data.nil? len = @_buf.length yield data = @_buf[len..-1] @_cache_store.set(key, data) else @_buf << data end nil end ## in template file <% cache_with("orders/#{@order.id}") do %> <p>Order ID: <%=h @order.id %></p> <p>Customer: <%=h @order.customer.name %></p> <% end %> Benchmark --------- Attached benchmark shows that called_from() is much faster than caller(). This is very important for logging or template timestamp check. $ ./ruby -s bench.rb -N=100000 user system total real caller()[0] 1.890000 0.010000 1.900000 ( 1.941812) caller()[0] (retrieve) 2.190000 0.010000 2.200000 ( 2.225966) called_from() 0.100000 0.000000 0.100000 ( 0.102810) called_from() (retrieve) 0.100000 0.000000 0.100000 ( 0.102133) Another Solutions ----------------- Adding new gobal function may be refused. The followings are another solutions instead of new global function. * Extend caller() to take 'count' parameter. For example: start = 1 count = 1 caller(start, count) #=> ["filename:linenum in `method'"] * Extend caller() to take 'conbine' flag. For example: start = 1 count = nil conbine = false caller(start, count, conbine) #=> [["filename", linenum, "method"], # ["filename", linenum, "method"], # .... ] * Add new standard library 'called_from.so' instead of Kernel#called_from(). ??°ã???????°ã?ã?¼ã????«é?¢æ?°ã??å° ?¥ã???????®ã?¯æ??çµ¶ã??????????½æ?§ã??é«??????? ?????®å?´å????¯ã??caller()?????¡å¼µ??????called_from()??¸å????®ã????¨ã????§ã?????????????? ?????¦ã?????????????¨ã?????????????? ???????????? Kernel#called_from() ??§ã?¯ã?ªã???¦ã?? called_from.so ???æ¨?æº?æ·»ä?? ????????¹é????§ã??????????? Note ---- * I tried to implement the above solutions, but failed because vm_backtrace_each() seems to search stack frames in the reverse order of what called_from() requires. * I can implement called_from() as user library in Ruby 1.8. http://rubygems.org/gems/called_from It is allowed to access to stack frame in Ruby 1.8, but no in 1.9. This is why I submit this propose. * å®???¯ä??è¨????another solutions???å®?è£????????????¨ã???????????called_from() ??§ã?? ??´è????®ã?¹ã?¿ã????¯ã????¬ã?¼ã????????辿ã??????????®ã?«å¯¾??????vm_backtrace_each() ??? ?????®é????ªã?§è¾¿????????¨ã???????§ã????ªã??????????ªã?®ã?§ã??å®?è£????諦ã???????? * Ruby 1.8 ??§ã?¯æ?¡å¼µ??¢ã?¸ã?¥ã?¼ã?«ã???????¹ã?¿ã????¯ã????¬ã?¼ã????«ã?¢ã?¯ã?»ã?¹ã?§ã???????®ã?? ??©ã?¤ã????©ã?ªã?¨ã????¦å??è£?????????? http://rubygems.org/gems/called_from ??????1.9??§ã?¯ã?¹ã?¿ã????¯ã????¬ã?¼ã????¸ã?®ã?¢ã?¯ã?»ã?¹ã????§ã????ªã????®ã?§ã????©ã?¤ã????©ã?ªã?? ä½??????ªã??????????®ã?????ä»?????????®ã???????ªæ??æ¡?????????¦ã?¿ã????? =end -- http://bugs.ruby-lang.org/