Adjusting conditions

Most of the execution time is taken up by the pattern-matching process. Consequently, the most efficient way of improving performance in rulesets consists in modifying the condition part of rules.

Ordering tests

The order in which tests are carried out in rule conditions affects the performance of the pattern-matching process. To get the best performance for a specific condition, place the most discriminant tests at the start. This position accelerates the selection of objects in the discrimination tree for tests that involve constants. Also, when you place the most discriminant tests at the start, you know sooner when an object does not satisfy the tests between conditions in the joins.

Sharing conditions

If the first N conditions of two rules are identical, these rules share the same portion of the network. As a consequence, searches for objects that satisfy these first N conditions are performed only once for all the rules that share these conditions. It is therefore a good idea to modify the order of rule conditions so that they share the network as much as possible.

Note:

You cannot share conditions in the sequential execution mode.

Ordering conditions in rules

Place the most selective conditions, or groups of conditions, at the beginning of tests. In this way, the objects that prevent a rule from being executed are removed sooner in the process. Tokens spend less time in the RetePlus network, joins require less memory, and fewer tests are necessary.

Example of the effect of condition order on RetePlus performance

Here is a rule with three object classes.

rule filter {
   when {
      A (a1==3; ?x:a2);
      B (b1==2; ?y:b2; b3==?x);
      C (c1==?y);
   }
   then {
      System.out.println("filter");
   }
}

Let’s work on this example.

  1. Imagine different contents for the working memory:

    • There are five objects of class A and their a2 attribute is 10.

    • There is an object of class B whose b2 attribute is 7 and b3 attribute is 10.

    • There is an object of class B whose b2 attribute is 4 and b3 attribute is 10.

    • There are 28 objects of class B. Their b3 attribute is 10 and their b2 attribute is neither 4 nor 7. In other words, there are thirty class B objects in all.

    • There is one object of class C and its c1 attribute is 4.

  2. If you were to draw the corresponding RetePlus network, the contents of the first join node would be quite different.

  3. It now contains 155 pairs of objects, which can be described like this:

    • Five pairs are formed by the objects of class A and the object of class B whose b2 is 7 and b3 is 10.

    • 150 pairs (that is, (5 * (28 + 1 + 1))) are formed by the objects of class A and the objects of class B whose b3 attribute is 10.

  4. If you now add an object of class C whose c1 attribute is 7, the work of the second join node consists of performing 155 tests, between the B object of each of the 155 pairs of the first join and the C object that you have just added, to verify that attributes b2 and c1 have the same value.

  5. Now suppose that you create filter2 rule by changing the order of the conditions of the filter rule so that it appears like this:

    rule filter2 {
       when {
          C (?y:c1);
          B (b1==2; b2==?y; ?x:b3);
          A (a1==3; a2==?x);
              }
       then {
          System.out.println("filter2");
       }
    }

    For the same initial working memory as before, the first join node, corresponding to the first two conditions, now contains 30 (instead of 155) pairs, formed by the class C object and the 30 class B objects whose b2 attribute is 4 and b3 attribute is 10.

  6. If you now add an object of the C class whose c1 attribute is 7, the work of the first join node consists of performing 30 tests between the newly added object and the 30 class B objects in memory. This causes us to add a new pair, formed by the C object that you have just added and the B object whose b2 attribute is 7.

  7. After all this is done, the second join node is activated. It performs 30 tests, between the B object of each pair from the previous join and the A object in memory, to verify that b3 and a2 have the same value.

These two rules, which produce the same result, have quite different computing costs.