[Groonga-commit] groonga/groonga at 4e32a94 [master] expression_rewriter optimizer: rewrite by ExpressionTree

Back to archive index

Kouhei Sutou null+****@clear*****
Thu Apr 7 12:46:03 JST 2016


Kouhei Sutou	2016-04-07 12:46:03 +0900 (Thu, 07 Apr 2016)

  New Revision: 4e32a94e311533390d254b14262a3cf44af314a7
  https://github.com/groonga/groonga/commit/4e32a94e311533390d254b14262a3cf44af314a7

  Message:
    expression_rewriter optimizer: rewrite by ExpressionTree

  Added files:
    lib/mrb/scripts/expression_tree/constant.rb
    lib/mrb/scripts/expression_tree/function_call.rb
    lib/mrb/scripts/expression_tree/variable.rb
  Removed files:
    lib/mrb/scripts/expression_tree/value.rb
  Modified files:
    lib/mrb/scripts/expression_tree.rb
    lib/mrb/scripts/expression_tree/binary_operation.rb
    lib/mrb/scripts/expression_tree/logical_operation.rb
    lib/mrb/scripts/expression_tree/sources.am
    lib/mrb/scripts/expression_tree_builder.rb
    plugins/expression_rewriters/optimizer.rb

  Modified: lib/mrb/scripts/expression_tree.rb (+3 -1)
===================================================================
--- lib/mrb/scripts/expression_tree.rb    2016-04-07 12:44:27 +0900 (d22baa5)
+++ lib/mrb/scripts/expression_tree.rb    2016-04-07 12:46:03 +0900 (ccc1ef5)
@@ -1,3 +1,5 @@
+require "expression_tree/constant"
 require "expression_tree/binary_operation"
+require "expression_tree/function_call"
 require "expression_tree/logical_operation"
-require "expression_tree/value"
+require "expression_tree/variable"

  Modified: lib/mrb/scripts/expression_tree/binary_operation.rb (+6 -0)
===================================================================
--- lib/mrb/scripts/expression_tree/binary_operation.rb    2016-04-07 12:44:27 +0900 (d725d69)
+++ lib/mrb/scripts/expression_tree/binary_operation.rb    2016-04-07 12:46:03 +0900 (897c02d)
@@ -9,6 +9,12 @@ module Groonga
         @left = left
         @right = right
       end
+
+      def build(expression)
+        @left.build(expression)
+        @right.build(expression)
+        expression.append_operator(@operator, 2)
+      end
     end
   end
 end

  Added: lib/mrb/scripts/expression_tree/constant.rb (+14 -0) 100644
===================================================================
--- /dev/null
+++ lib/mrb/scripts/expression_tree/constant.rb    2016-04-07 12:46:03 +0900 (8e7e0cc)
@@ -0,0 +1,14 @@
+module Groonga
+  module ExpressionTree
+    class Constant
+      attr_reader :value
+      def initialize(value)
+        @value = value
+      end
+
+      def build(expression)
+        expression.append_constant(@value, Operator::PUSH, 1)
+      end
+    end
+  end
+end

  Added: lib/mrb/scripts/expression_tree/function_call.rb (+20 -0) 100644
===================================================================
--- /dev/null
+++ lib/mrb/scripts/expression_tree/function_call.rb    2016-04-07 12:46:03 +0900 (b3bfff6)
@@ -0,0 +1,20 @@
+module Groonga
+  module ExpressionTree
+    class FunctionCall
+      attr_reader :procedure
+      attr_reader :arguments
+      def initialize(procedure, arguments)
+        @procedure = procedure
+        @arguments = arguments
+      end
+
+      def build(expression)
+        expression.append_object(@procedure, Operator::PUSH, 1)
+        @arguments.each do |argument|
+          argument.build(expression)
+        end
+        expression.append_operator(Operator::CALL, @arguments.size)
+      end
+    end
+  end
+end

  Modified: lib/mrb/scripts/expression_tree/logical_operation.rb (+7 -0)
===================================================================
--- lib/mrb/scripts/expression_tree/logical_operation.rb    2016-04-07 12:44:27 +0900 (eeacdb0)
+++ lib/mrb/scripts/expression_tree/logical_operation.rb    2016-04-07 12:46:03 +0900 (ef95ccb)
@@ -7,6 +7,13 @@ module Groonga
         @operator = operator
         @nodes = nodes
       end
+
+      def build(expression)
+        @nodes.each_with_index do |node, i|
+          node.build(expression)
+          expression.append_operator(@operator, 2) if i > 1
+        end
+      end
     end
   end
 end

  Modified: lib/mrb/scripts/expression_tree/sources.am (+3 -1)
===================================================================
--- lib/mrb/scripts/expression_tree/sources.am    2016-04-07 12:44:27 +0900 (184be7c)
+++ lib/mrb/scripts/expression_tree/sources.am    2016-04-07 12:46:03 +0900 (6fd10bd)
@@ -1,4 +1,6 @@
 RUBY_SCRIPT_FILES =				\
 	binary_operation.rb			\
+	constant.rb				\
+	function_call.rb			\
 	logical_operation.rb			\
-	value.rb
+	variable.rb

  Deleted: lib/mrb/scripts/expression_tree/value.rb (+0 -10) 100644
===================================================================
--- lib/mrb/scripts/expression_tree/value.rb    2016-04-07 12:44:27 +0900 (df0e211)
+++ /dev/null
@@ -1,10 +0,0 @@
-module Groonga
-  module ExpressionTree
-    class Value
-      attr_reader :code
-      def initialize(code)
-        @code = code
-      end
-    end
-  end
-end

  Added: lib/mrb/scripts/expression_tree/variable.rb (+14 -0) 100644
===================================================================
--- /dev/null
+++ lib/mrb/scripts/expression_tree/variable.rb    2016-04-07 12:46:03 +0900 (bc5e523)
@@ -0,0 +1,14 @@
+module Groonga
+  module ExpressionTree
+    class Variable
+      attr_reader :column
+      def initialize(column)
+        @column = column
+      end
+
+      def build(expression)
+        expression.append_object(@column, Operator::GET_VALUE, 1)
+      end
+    end
+  end
+end

  Modified: lib/mrb/scripts/expression_tree_builder.rb (+5 -2)
===================================================================
--- lib/mrb/scripts/expression_tree_builder.rb    2016-04-07 12:44:27 +0900 (1cf5da5)
+++ lib/mrb/scripts/expression_tree_builder.rb    2016-04-07 12:46:03 +0900 (91ed2f1)
@@ -66,8 +66,11 @@ module Groonga
           left = stack.pop
           node = ExpressionTree::BinaryOperation.new(code.op, left, right)
           stack.push(node)
-        when Operator::GET_VALUE, Operator::PUSH
-          node = ExpressionTree::Value.new(code)
+        when Operator::GET_VALUE
+          node = ExpressionTree::Variable.new(code.value)
+          stack.push(node)
+        when Operator::PUSH
+          node = ExpressionTree::Constant.new(code.value.value)
           stack.push(node)
         else
           raise "unknown operator: #{code.inspect}"

  Modified: plugins/expression_rewriters/optimizer.rb (+131 -22)
===================================================================
--- plugins/expression_rewriters/optimizer.rb    2016-04-07 12:44:27 +0900 (bcc2088)
+++ plugins/expression_rewriters/optimizer.rb    2016-04-07 12:46:03 +0900 (ed437ba)
@@ -4,34 +4,143 @@ module Groonga
       register "optimizer"
 
       def rewrite
-        codes =****@expre*****
-        n_codes = codes.size
+        builder = ExpressionTreeBuilder.new(@expression)
+        root_node = builder.build
 
-        # (A >= x && A < y) -> between(A, x, "include", y, "exclude")
-        return nil if n_codes != 7
+        optimized_root_node = optimize_node(root_node)
 
-        return nil if codes[6].op != Operator::AND
+        variable = @expression[0]
+        rewritten = Expression.create(context[variable.domain])
+        optimized_root_node.build(rewritten)
+        rewritten
+      end
 
-        return nil if codes[0].op != Operator::GET_VALUE
-        return nil if codes[1].op != Operator::PUSH
-        return nil if codes[2].op != Operator::GREATER_EQUAL
+      private
+      def optimize_node(node)
+        case node
+        when ExpressionTree::LogicalOperation
+          optimized_sub_nodes = node.nodes.collect do |sub_node|
+            optimize_node(sub_node)
+          end
+          case node.operator
+          when Operator::AND
+            optimized_sub_nodes = optimize_and_sub_nodes(optimized_sub_nodes)
+          end
+          ExpressionTree::LogicalOperation.new(node.operator,
+                                               optimized_sub_nodes)
+        when ExpressionTree::BinaryOperation
+          optimized_left = optimize_node(node.left)
+          optimized_right = optimize_node(node.right)
+          if optimized_left.is_a?(ExpressionTree::Constant) and
+              optimized_right.is_a?(ExpressionTree::Variable)
+            ExpressionTree::BinaryOperation.new(node.operator,
+                                                optimized_right,
+                                                optimized_left)
+          elsif node.left == optimized_left and node.right == optimized_right
+            node
+          else
+            ExpressionTree::BinaryOperation.new(node.operator,
+                                                optimized_left,
+                                                optimized_right)
+          end
+        else
+          node
+        end
+      end
 
-        return nil if codes[3].op != Operator::GET_VALUE
-        return nil if codes[4].op != Operator::PUSH
-        return nil if codes[5].op != Operator::LESS
+      def optimize_and_sub_nodes(sub_nodes)
+        grouped_sub_nodes = sub_nodes.group_by do |sub_node|
+          case sub_node
+          when ExpressionTree::BinaryOperation
+            if sub_node.left.is_a?(ExpressionTree::Variable)
+              sub_node.left.column
+            else
+              nil
+            end
+          else
+            nil
+          end
+        end
 
-        return nil if codes[3].value != codes[0].value
+        optimized_nodes = []
+        grouped_sub_nodes.each do |column, grouped_nodes|
+          if column
+            grouped_nodes = optimize_grouped_nodes(column, grouped_nodes)
+          end
+          optimized_nodes.concat(grouped_nodes)
+        end
 
-        variable = @expression[0]
-        rewritten = Expression.create(context[variable.domain])
-        rewritten.append_object(Context.instance["between"], Operator::PUSH, 1)
-        rewritten.append_object(codes[0].value, Operator::GET_VALUE, 1)
-        rewritten.append_constant(codes[1].value.value, Operator::PUSH, 1)
-        rewritten.append_constant("include", Operator::PUSH, 1)
-        rewritten.append_constant(codes[4].value.value, Operator::PUSH, 1)
-        rewritten.append_constant("exclude", Operator::PUSH, 1)
-        rewritten.append_operator(Operator::CALL, 5)
-        rewritten
+        optimized_nodes
+        # TODO
+        # optimized_nodes.sort_by do |node|
+        #   node.estimate_size
+        # end
+      end
+
+      COMPARISON_OPERATORS = [
+        Operator::EQUAL,
+        Operator::NOT_EQUAL,
+        Operator::LESS,
+        Operator::GREATER,
+        Operator::LESS_EQUAL,
+        Operator::GREATER_EQUAL,
+      ]
+      def optimize_grouped_nodes(column, grouped_nodes)
+        target_nodes, done_nodes = grouped_nodes.partition do |node|
+          node.is_a?(ExpressionTree::BinaryOperation) and
+            COMPARISON_OPERATORS.include?(node.operator) and
+            node.right.is_a?(ExpressionTree::Constant)
+        end
+
+        # TODO: target_nodes = remove_needless_nodes(target_nodes)
+        # e.g.: x < 1 && x < 3 -> x < 1: (x < 3) is meaningless
+
+        if target_nodes.size == 2
+          between_node = try_optimize_between(column, target_nodes)
+          if between_node
+            done_nodes << between_node
+          else
+            done_nodes.concat(target_nodes)
+          end
+        else
+          done_nodes.concat(target_nodes)
+        end
+
+        done_nodes
+      end
+
+      def try_optimize_between(column, target_nodes)
+        greater_node = nil
+        less_node = nil
+        target_nodes.each do |node|
+          case node.operator
+          when Operator::GREATER, Operator::GREATER_EQUAL
+            greater_node = node
+          when Operator::LESS, Operator::LESS_EQUAL
+            less_node = node
+          end
+        end
+        return nil if greater_node.nil? or less_node.nil?
+
+        between = context["between"]
+        if greater_node.operator == Operator::GREATER
+          greater_border = "exclude"
+        else
+          greater_border = "include"
+        end
+        if less_node.operator == Operator::LESS
+          less_border = "exclude"
+        else
+          less_border = "include"
+        end
+        arguments = [
+          ExpressionTree::Variable.new(column),
+          greater_node.right,
+          ExpressionTree::Constant.new(greater_border),
+          less_node.right,
+          ExpressionTree::Constant.new(less_border),
+        ]
+        ExpressionTree::FunctionCall.new(between, arguments)
       end
     end
   end
-------------- next part --------------
HTML����������������������������...
다운로드 



More information about the Groonga-commit mailing list
Back to archive index