Skip to content

Performance Profiling and Debugging

A classic rite of passage for a Rave developer is writing a rule that perfectly passes all unit tests, but when deployed to the Optimization Solver, causes the run time to balloon from 2 hours to 48 hours.

Because Rave is heavily declarative and relies on traversers, it is very easy to accidentally write operations with \(O(N^3)\) or worse time complexity.


1. The Danger of Nested Traversers

The most common performance killer is nesting broad traversers inside one another.

The Anti-Pattern:

// Iterating over every leg in a month-long roster
%has_long_duty% = 
  any(leg(roster), 
      // And for EVERY leg, traversing the entire duty it belongs to!
      sum(leg(duty), leg.%flight_time%) > 10:00
  );
If a roster has 60 legs, this requires \(60 \times 4\) (average legs per duty) evaluations. Now multiply that by 5,000 crew members, evaluated millions of times during column generation. The solver will grind to a halt.

The Optimized Solution: Traverse at the highest appropriate level.

%has_long_duty% = 
  any(duty(roster), 
      // The sum is evaluated once per duty, not once per leg
      sum(leg(duty), leg.%flight_time%) > 10:00
  );

2. Leveraging Caching and Memoization

Behind the scenes, the Rave compiler tries to optimize execution, but it relies on you to structure variables efficiently.

If you use a complex calculation multiple times, assign it to a variable. The Rave evaluation engine caches (memoizes) the variable's state for the current search tree node.

// BAD: Re-evaluating the traverser twice
%is_high_penalty% = 
  if sum(leg(duty), leg.%delay_minutes%) > 60 then
     sum(leg(duty), leg.%delay_minutes%) * 10.0
  else
     0.0;

// GOOD: Evaluated once and cached
%total_delay% = sum(leg(duty), leg.%delay_minutes%);

%is_high_penalty% = 
  if %total_delay% > 60 then
     %total_delay% * 10.0
  else
     0.0;

3. Profiling Tools (Rule Analyzer)

When a generation run is slow, developers use Jeppesen's internal profiling tools (often referred to as the Rule Analyzer or Performance Logs).

These tools output reports showing:

  1. Hit Count: How many millions of times a rule was evaluated.
  2. Prune Rate: How many times a Final Rule successfully killed a branch.
  3. Execution Time (ms): The total CPU time spent evaluating a specific variable.

Optimization Strategy: If a rule has a massive execution time but a 0.01% prune rate, it is mathematically inefficient. It is costing the solver massive CPU cycles without actually helping to narrow down the search space. Developers must rewrite the rule to be more restrictive earlier in the tree, or simplify the math.