[前][次][番号順一覧][スレッド一覧]

ruby-changes:64374

From: aycabta <ko1@a...>
Date: Sun, 20 Dec 2020 16:34:16 +0900 (JST)
Subject: [ruby-changes:64374] 9f08e3c703 (master): [ruby/irb] Add measure command

https://git.ruby-lang.org/ruby.git/commit/?id=9f08e3c703

From 9f08e3c703795e81d333d568e7e44743022468f1 Mon Sep 17 00:00:00 2001
From: aycabta <aycabta@g...>
Date: Sat, 15 Aug 2020 06:36:24 +0900
Subject: [ruby/irb] Add measure command

You can use "measure" command to check performance in IRB like below:

  irb(main):001:0> 3
  => 3
  irb(main):002:0> measure
  TIME is added.
  => nil
  irb(main):003:0> 3
  processing time: 0.000058s
  => 3
  irb(main):004:0> measure :off
  => nil
  irb(main):005:0> 3
  => 3

You can set "measure :on" by "IRB.conf[:MEASURE] = true" in .irbrc, and, also,
set custom performance check method:

  IRB.conf[:MEASURE_PROC][:CUSTOM] = proc { |context, code, line_no, &block|
    time = Time.now
    result = block.()
    now = Time.now
    puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
    result
  }

https://github.com/ruby/irb/commit/3899eaf2e2

diff --git a/lib/irb.rb b/lib/irb.rb
index 579fd67..26c5d2e 100644
--- a/lib/irb.rb
+++ b/lib/irb.rb
@@ -538,7 +538,23 @@ module IRB https://github.com/ruby/ruby/blob/trunk/lib/irb.rb#L538
         signal_status(:IN_EVAL) do
           begin
             line.untaint if RUBY_VERSION < '2.7'
-            @context.evaluate(line, line_no, exception: exc)
+            if IRB.conf[:MEASURE] && IRB.conf[:MEASURE_CALLBACKS].empty?
+              IRB.set_measure_callback
+            end
+            if IRB.conf[:MEASURE] && !IRB.conf[:MEASURE_CALLBACKS].empty?
+              result = nil
+              last_proc = proc{ result = @context.evaluate(line, line_no, exception: exc) }
+              IRB.conf[:MEASURE_CALLBACKS].map{ |s| s.last }.inject(last_proc) { |chain, item|
+                proc {
+                  item.(@context, line, line_no, exception: exc) do
+                    chain.call
+                  end
+                }
+              }.call
+              @context.set_last_value(result)
+            else
+              @context.evaluate(line, line_no, exception: exc)
+            end
             if @context.echo?
               if assignment_expression?(line)
                 if @context.echo_on_assignment?
diff --git a/lib/irb/cmd/measure.rb b/lib/irb/cmd/measure.rb
new file mode 100644
index 0000000..6161d15
--- /dev/null
+++ b/lib/irb/cmd/measure.rb
@@ -0,0 +1,34 @@ https://github.com/ruby/ruby/blob/trunk/lib/irb/cmd/measure.rb#L1
+require_relative "nop"
+
+# :stopdoc:
+module IRB
+  module ExtendCommand
+    class Measure < Nop
+      def initialize(*args)
+        super(*args)
+      end
+
+      def execute(type = nil, arg = nil)
+        case type
+        when :off
+          IRB.conf[:MEASURE] = nil
+          IRB.unset_measure_callback(arg)
+        when :list
+          IRB.conf[:MEASURE_CALLBACKS].each do |type_name, _|
+            puts "- #{type_name}"
+          end
+        when :on
+          IRB.conf[:MEASURE] = true
+          added = IRB.set_measure_callback(type)
+          puts "#{added.first} is added."
+        else
+          IRB.conf[:MEASURE] = true
+          added = IRB.set_measure_callback(type)
+          puts "#{added.first} is added."
+        end
+        nil
+      end
+    end
+  end
+end
+# :startdoc:
diff --git a/lib/irb/extend-command.rb b/lib/irb/extend-command.rb
index 3cd0c51..55e7b3e 100644
--- a/lib/irb/extend-command.rb
+++ b/lib/irb/extend-command.rb
@@ -125,6 +125,10 @@ module IRB # :nodoc: https://github.com/ruby/ruby/blob/trunk/lib/irb/extend-command.rb#L125
         :irb_info, :Info, "irb/cmd/info"
       ],
 
+      [
+        :measure, :Measure, "irb/cmd/measure"
+      ],
+
     ]
 
     # Installs the default irb commands:
diff --git a/lib/irb/init.rb b/lib/irb/init.rb
index 73ef420..f17d316 100644
--- a/lib/irb/init.rb
+++ b/lib/irb/init.rb
@@ -111,11 +111,66 @@ module IRB # :nodoc: https://github.com/ruby/ruby/blob/trunk/lib/irb/init.rb#L111
     @CONF[:CONTEXT_MODE] = 4 # use a copy of TOPLEVEL_BINDING
     @CONF[:SINGLE_IRB] = false
 
+    @CONF[:MEASURE] = false
+    @CONF[:MEASURE_PROC] = {}
+    @CONF[:MEASURE_PROC][:TIME] = proc { |context, code, line_no, &block|
+      time = Time.now
+      result = block.()
+      now = Time.now
+      puts 'processing time: %fs' % (now - time) if IRB.conf[:MEASURE]
+      result
+    }
+    @CONF[:MEASURE_PROC][:STACKPROF] = proc { |context, code, line_no, &block|
+      success = false
+      begin
+        require 'stackprof'
+        success = true
+      rescue LoadError
+        puts 'Please run "gem install stackprof" before measuring by StackProf.'
+      end
+      if success
+        result = nil
+        stackprof_result = StackProf.run(mode: :cpu) do
+          result = block.()
+        end
+        StackProf::Report.new(stackprof_result).print_text if IRB.conf[:MEASURE]
+        result
+      else
+        block.()
+      end
+    }
+    @CONF[:MEASURE_CALLBACKS] = []
+
     @CONF[:LC_MESSAGES] = Locale.new
 
     @CONF[:AT_EXIT] = []
   end
 
+  def IRB.set_measure_callback(type = nil)
+    added = nil
+    if type
+      type_sym = type.upcase.to_sym
+      if IRB.conf[:MEASURE_PROC][type_sym]
+        added = [type_sym, IRB.conf[:MEASURE_PROC][type_sym]]
+      end
+    elsif IRB.conf[:MEASURE_PROC][:CUSTOM]
+      added = [:CUSTOM, IRB.conf[:MEASURE_PROC][:CUSTOM]]
+    else
+      added = [:TIME, IRB.conf[:MEASURE_PROC][:TIME]]
+    end
+    IRB.conf[:MEASURE_CALLBACKS] << added if added
+    added
+  end
+
+  def IRB.unset_measure_callback(type = nil)
+    if type.nil?
+      IRB.conf[:MEASURE_CALLBACKS].clear
+    else
+      type_sym = type.upcase.to_sym
+      IRB.conf[:MEASURE_CALLBACKS].reject!{ |t, c| t == type_sym }
+    end
+  end
+
   def IRB.init_error
     @CONF[:LC_MESSAGES].load("irb/error.rb")
   end
diff --git a/test/irb/test_cmd.rb b/test/irb/test_cmd.rb
index bb33f53..6ecb5f3 100644
--- a/test/irb/test_cmd.rb
+++ b/test/irb/test_cmd.rb
@@ -123,5 +123,149 @@ module TestIRB https://github.com/ruby/ruby/blob/trunk/test/irb/test_cmd.rb#L123
       IRB.__send__(:remove_const, :IRBRC_EXT)
       IRB.const_set(:IRBRC_EXT, ext_backup)
     end
+
+    class TestInputMethod < ::IRB::InputMethod
+      attr_reader :list, :line_no
+
+      def initialize(list = [])
+        super("test")
+        @line_no = 0
+        @list = list
+      end
+
+      def gets
+        @list[@line_no]&.tap {@line_no += 1}
+      end
+
+      def eof?
+        @line_no >= @list.size
+      end
+
+      def encoding
+        Encoding.default_external
+      end
+
+      def reset
+        @line_no = 0
+      end
+    end
+
+    def test_measure
+      IRB.init_config(nil)
+      IRB.conf[:PROMPT] = {
+        DEFAULT: {
+          PROMPT_I: '> ',
+          PROMPT_S: '> ',
+          PROMPT_C: '> ',
+          PROMPT_N: '> '
+        }
+      }
+      IRB.conf[:MEASURE] = false
+      input = TestInputMethod.new([
+        "3\n",
+        "measure\n",
+        "3\n",
+        "measure :off\n",
+        "3\n",
+      ])
+      irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
+      irb.context.return_format = "=> %s\n"
+      out, err = capture_output do
+        irb.eval_input
+      end
+      assert_empty err
+      assert_match(/\A=> 3\nTIME is added\.\n=> nil\nprocessing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+    end
+
+    def test_measure_enabled_by_rc
+      IRB.init_config(nil)
+      IRB.conf[:PROMPT] = {
+        DEFAULT: {
+          PROMPT_I: '> ',
+          PROMPT_S: '> ',
+          PROMPT_C: '> ',
+          PROMPT_N: '> '
+        }
+      }
+      IRB.conf[:MEASURE] = true
+      input = TestInputMethod.new([
+        "3\n",
+        "measure :off\n",
+        "3\n",
+      ])
+      irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
+      irb.context.return_format = "=> %s\n"
+      out, err = capture_output do
+        irb.eval_input
+      end
+      assert_empty err
+      assert_match(/\Aprocessing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+    end
+
+    def test_measure_enabled_by_rc_with_custom
+      IRB.init_config(nil)
+      IRB.conf[:PROMPT] = {
+        DEFAULT: {
+          PROMPT_I: '> ',
+          PROMPT_S: '> ',
+          PROMPT_C: '> ',
+          PROMPT_N: '> '
+        }
+      }
+      IRB.conf[:MEASURE] = true
+      IRB.conf[:MEASURE_PROC][:CUSTOM] = proc { |line, line_no, &block|
+        time = Time.now
+        result = block.()
+        now = Time.now
+        puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
+        result
+      }
+      input = TestInputMethod.new([
+        "3\n",
+        "measure :off\n",
+        "3\n",
+      ])
+      irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
+      irb.context.return_format = "=> %s\n"
+      out, err = capture_output do
+        irb.eval_input
+      end
+      assert_empty err
+      assert_match(/\Acustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+    end
+
+    def test_measure_with_custom
+      IRB.init_config(nil)
+      IRB.conf[:PROMPT] = {
+        DEFAULT: {
+          PROMPT_I: '> ',
+          PROMPT_S: '> ',
+          PROMPT_C: '> ',
+          PROMPT_N: '> '
+        }
+      }
+      IRB.conf[:MEASURE] = false
+      IRB.conf[:MEASURE_PROC][:CUSTOM] = proc { |line, line_no, &block|
+        time = Time.now
+        result = block.()
+        now = Time.now
+        puts 'custom processing time: %fs' % (Time.now - time) if IRB.conf[:MEASURE]
+        result
+      }
+      input = TestInputMethod.new([
+        "3\n",
+        "measure\n",
+        "3\n",
+        "measure :off\n",
+        "3\n",
+      ])
+      irb = IRB::Irb.new(IRB::WorkSpace.new(Object.new), input)
+      irb.context.return_format = "=> %s\n"
+      out, err = capture_output do
+        irb.eval_input
+      end
+      assert_empty err
+      assert_match(/\A=> 3\nCUSTOM is added\.\n=> nil\ncustom processing time: .+\n=> 3\n=> nil\n=> 3\n/, out)
+    end
   end
 end
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]