September 5, 2022
3
min

Bavet - A faster score engine for OptaPlanner

Tom Cools
Keep up with Timefold
Product updates, optimization insights, and stories from the builders behind PlanningAI.
Share

Drools is an extremely fast rule engine. Under the hood, OptaPlanner has used Drools as a score engine for ages. Today, we’re announcing a faster, lightweight alternative: Bavet.

Bavet is a feature of OptaPlanner. It is not a rule engine. It is a pure, single-purpose, incremental score calculation implementation of the ConstraintStreams API. Bavet is feature complete as of OptaPlanner 8.27.0.Final. You can switch from Drools to Bavet in a single line of code.

Twice as fast score calculation. Zero API changes.

Faster

For 20 diverse use cases, we compared Bavet and Drools for OptaPlanner score calculation. We ran JMH benchmarks on OpenJDK 17 (ParallelGC, Xmx1G) on a stable benchmark machine (Intel® Xeon® Silver (12 cores total / 24 threads) and 128 GiB RAM memory) without any other computational demanding processes running.

On average, Bavet is twice as fast as Drools for score calculation. In the Vehicle Routing Problem, Bavet is even three times as fast as Drools:

Use caseDroolsBavetSpeed upAverage:53,644136,765+132%cheaptime4,34914,543+234%cloudbalancing162,820608,204+274%coachShuttleGathering38,543111,991+191%conferenceScheduling1,0721,264+18%curriculumCourse32,27238,933+21%examination11,82125,712+118%flightCrewScheduling97,020126,563+30%investment68,935401,806+483%machineReassignment13,38428,619+114%meetingscheduling2,2912,158-6%nQueens177,528285,268+61%nurserostering10,65721,090+98%pas50,97147,551-7%projectjobscheduling23,71578,291+230%rockTour33,997152,472+348%taskAssigning10,53120,680+96%tennis106,172236,437+123%travelingtournament49,42877,143+56%tsp169,125430,384+154%vehicleRouting8,24726,187+218%Table 1. Score calculation speed on different use cases

Bavet is faster than Drools for 90% of the use cases. Of course, your mileage may vary. Turn on Bavet and if it’s not faster in your use case, let us know.

Drools and Bavet are both still improving. This performance race is far from over.

Scaling

Does Bavet scale well?

On commodity hardware, we ran a 5 minutes VRP benchmark on different dataset sizes, to compare how Drools and Bavet scale up:

belgium-n50-k10belgium-n100-k10belgium-n500-k20belgium-n1000-k20belgium-n2750-k55AverageDrools76,919/s58,365/s36,609/s23,394/s29,770/s45,011/sBavet307,290/s242,400/s147,595/s89,850/s91,115/s175,650/sSpeed up+299.50%+315.32%+303.17%+284.07%+206.06%+290.24%Table 2. Score calculation speed on different dataset sizes of VRP

Same story, but the performance gap does close as the scale goes up.

Not a rule engine

Bavet is not a rule engine. It deliberately doesn’t support inference, nor Complex Event Processing (CEP), nor other common business rule engine features:

OptaPlanner only requires a score engine. Its Drools implementation only uses a small subset of Drools’s features. Bavet on the other hand, is a score engine tailored to OptaPlanner. It’s part of OptaPlanner. It has no use outside of OptaPlanner.

For incremental score calculation, Bavet borrows techniques from the RETE algorithm and Drools’s Phreak algorithm. For example, the JoinNode in Bavet contains insert(), update() and retract() methods. But below the surface, it’s a very different implementation. Compare it with the method signatures of similar methods in the JoinNode in Drools.

History and naming

I created Bavet as a POC in 2019 and added it into OptaPlanner as an experimental, fast, incomplete feature. There it sat frozen. For 3 years. Until recently, when Lukáš Petrovický and me completed all missing features and refactored it to the performance sensation is today.

Naming wise, bavet is a Flemish (Dutch) slang word for a bib. Very useful if your baby is drooling. I came up with that name when we were eating with our kids at a spaghetti restaurant called Bavet, while facing this mural:

Ok, maybe I didn’t put much effort into that.

But it doesn’t really need a good name. It’s just one of OptaPlanner’s score calculation options. An implementation detail, really.

Stability

We believe Bavet is very stable. We successfully run our 48+ hours stress tests on Bavet regularly. These stress tests stomp out score corruption by solving a lot of datasets across many use cases.

More lightweight

In OpenShift and Kubernetes clouds, the size of pods matter. By using Bavet, you can slim down OptaPlanner’s classpath to exclude the Drools dependencies. The Bavet jar is 400 KB.

On the OptaPlanner hello-world quickstart, a Maven assembly of jar-with-dependencies with only Bavet included is 10 MB smaller:

Core dependenciesSizeReductionCore exclusionsAll (default)17.5 MB0%noneDrools CS only17.1 MB-2%optaplanner-constraint-drl, optaplanner-constraint-streams-bavetBavet CS only7.0 MB-60%optaplanner-constraint-drl, optaplanner-constraint-streams-droolsTable 3. Distribution size of jar-with-dependencies on OptaPlanner’s hello-world

By default, optaplanner-core includes both Drools and Bavet, so you have to explicitly exclude it in Maven or Gradle:

   <dependency>
     <groupId>org.optaplanner</groupId>
     <artifactId>optaplanner-core</artifactId>
     <exclusions>
       <exclusion>
         <groupId>org.optaplanner</groupId>
         <artifactId>optaplanner-constraint-drl</artifactId>
       </exclusion>
       <exclusion>
         <groupId>org.optaplanner</groupId>
         <artifactId>optaplanner-constraint-streams-drools</artifactId>
       </exclusion>
     </exclusions>
   </dependency>

This reduces optaplanner-core from 42 to 17 transitive dependencies. Specifically, all these jars are removed from your classpath:

\- org.optaplanner:optaplanner-constraint-streams-drools:...
  +- org.drools:drools-engine:...
  |  +- org.kie:kie-api:...
  |  +- org.kie:kie-internal:...
  |  +- org.drools:drools-core:...
  |  |  +- org.kie:kie-util-xml:...
  |  |  +- org.drools:drools-wiring-api:...
  |  |  +- org.drools:drools-wiring-static:...
  |  |  +- org.drools:drools-util:...
  |  |  \- commons-codec:commons-codec:...
  |  +- org.drools:drools-wiring-dynamic:...
  |  +- org.drools:drools-kiesession:...
  |  +- org.drools:drools-tms:...
  |  +- org.drools:drools-compiler:...
  |  |  +- org.drools:drools-drl-parser:...
  |  |  +- org.drools:drools-drl-extensions:...
  |  |  +- org.drools:drools-drl-ast:...
  |  |  +- org.kie:kie-memory-compiler:...
  |  |  +- org.drools:drools-ecj:...
  |  |  +- org.kie:kie-util-maven-support:...
  |  |  \- org.antlr:antlr-runtime:...
  |  +- org.drools:drools-model-compiler:...
  |  |  \- org.drools:drools-canonical-model:...
  |  \- org.drools:drools-model-codegen:...
  |     +- org.drools:drools-codegen-common:...
  |     +- com.github.javaparser:javaparser-core:...
  |     +- org.drools:drools-mvel-parser:...
  |     \- org.drools:drools-mvel-compiler:...
  \- org.drools:drools-alphanetwork-compiler:...

Bavet (optaplanner-constraint-streams-bavet) has no transitive dependencies (except for optaplanner-constraint-streams-common).

Try it out

First upgrade to OptaPlanner 8.27.0.Final or later, if you haven’t already. If you’re using the deprecated scoreDRL approach, migrate from scoreDRL to constraint streams first.

By default, OptaPlanner still uses Drools for constraint streams. To use Bavet instead, explicitly switch the ConstraintStreamImplType to BAVET:

Plain Java

Switch to Bavet in either your *.java file:

SolverFactory<TimeTable> solverFactory = SolverFactory.create(new SolverConfig()
       ...
       .withConstraintStreamImplType(ConstraintStreamImplType.BAVET)
       ...);

or in your solverConfig.xml:

 <scoreDirectorFactory>
   ...
   <constraintStreamImplType>BAVET</constraintStreamImplType>
 </scoreDirectorFactory>

Quarkus

Switch to Bavet in src/main/resources/application.properties:

quarkus.optaplanner.solver.constraintStreamImplType=BAVET

Spring

Switch to Bavet in src/main/resources/application.properties:

optaplanner.solver.constraintStreamImplType=BAVET

Share your results

Help us out. Try Bavet and let us know here how your score calculation speed changes. Look for the score calculation speed in the INFO log: it’s part of the Solving ended message.

Related blogs
June 25, 2026
0
min

Same coffee, same shoes, same plan: the beauty of no surprises

Optimization
Explainable AI
June 23, 2026
0
min

Timefold Raises $13M as AI Drives Demand for Routing and Scheduling APIs

Press
June 1, 2026
0
min

How upskilling technicians unlocks field service routing efficiency

Explainable AI
FSR
Platform
May 4, 2026
0
min

How much fuel can route optimization actually save?

FSR
Scheduling intelligence
Platform
May 1, 2026
0
min

Three optimizations to make your Timefold Solver faster

Solver
Open-source
April 24, 2026
0
min

Timefold Solver 2.0 upgrade: 15 projects, under 10 minutes

Solver
Open-source

When scheduling works, everything works.

Less waste. More control. Teams that trust the plan.