Drools is an especially quick rule engine. Below the hood, OptaPlanner has used Drools as a rating engine for ages. At this time, we’re asserting a sooner, light-weight different: Bavet.
Bavet is a function of OptaPlanner. It’s not a rule engine. It’s a pure, single-purpose, incremental rating calculation implementation of the ConstraintStreams API. Bavet is function full as of OptaPlanner 8.27.0.Remaining
. You’ll be able to change from Drools to Bavet in a single line of code.
Twice as quick rating calculation. Zero API adjustments.
Sooner
For 20 various use instances, we in contrast Bavet and Drools for OptaPlanner rating calculation. We ran JMH benchmarks on OpenJDK 17 (ParallelGC
, Xmx1G
) on a steady benchmark machine (Intel® Xeon® Silver (12 cores whole / 24 threads)
and 128 GiB
RAM reminiscence) with out every other computational demanding processes operating.
On common, Bavet is twice as quick as Drools for rating calculation. Within the Automobile Routing Drawback, Bavet is even 3 times as quick as Drools:
Use case | Drools | Bavet | Velocity up |
---|---|---|---|
cheaptime | 4,349 | 14,543 | +234% |
cloudbalancing | 162,820 | 608,204 | +274% |
coachShuttleGathering | 38,543 | 111,991 | +191% |
conferenceScheduling | 1,072 | 1,264 | +18% |
curriculumCourse | 32,272 | 38,933 | +21% |
examination | 11,821 | 25,712 | +118% |
flightCrewScheduling | 97,020 | 126,563 | +30% |
funding | 68,935 | 401,806 | +483% |
machineReassignment | 13,384 | 28,619 | +114% |
meetingscheduling | 2,291 | 2,158 | -6% |
nQueens | 177,528 | 285,268 | +61% |
nurserostering | 10,657 | 21,090 | +98% |
pas | 50,971 | 47,551 | -7% |
projectjobscheduling | 23,715 | 78,291 | +230% |
rockTour | 33,997 | 152,472 | +348% |
taskAssigning | 10,531 | 20,680 | +96% |
tennis | 106,172 | 236,437 | +123% |
travelingtournament | 49,428 | 77,143 | +56% |
tsp | 169,125 | 430,384 | +154% |
vehicleRouting | 8,247 | 26,187 | +218% |
Common: | 53,644 | 136,765 | +132% |
Bavet is quicker than Drools for 90% of the use instances. In fact, your mileage could fluctuate. Activate Bavet and if it’s not sooner in your use case, tell us.
Drools and Bavet are each nonetheless enhancing. This efficiency race is much from over.
Scaling
Does Bavet scale effectively?
On commodity {hardware}, we ran a 5 minutes VRP benchmark on completely different dataset sizes, to check how Drools and Bavet scale up:
belgium-n50-k10 | belgium-n100-k10 | belgium-n500-k20 | belgium-n1000-k20 | belgium-n2750-k55 | Common | |
---|---|---|---|---|---|---|
Drools | 76,919/s | 58,365/s | 36,609/s | 23,394/s | 29,770/s | 45,011/s |
Bavet | 307,290/s | 242,400/s | 147,595/s | 89,850/s | 91,115/s | 175,650/s |
Velocity up | +299.50% | +315.32% | +303.17% | +284.07% | +206.06% | +290.24% |
Similar story, however the efficiency hole does shut as the dimensions goes up.
Not a rule engine
Bavet is not a rule engine. It intentionally doesn’t help inference, nor Complicated Occasion Processing (CEP), nor different frequent enterprise rule engine options:
OptaPlanner solely requires a rating engine. Its Drools implementation solely makes use of a small subset of Drools’s options. Bavet however, is a rating engine tailor-made to OptaPlanner. It’s a part of OptaPlanner. It has no use outdoors of OptaPlanner.
For incremental rating calculation, Bavet borrows methods from the RETE algorithm and Drools’s Phreak algorithm. For instance, the JoinNode in Bavet comprises insert()
, replace()
and retract()
strategies. However under the floor, it’s a really completely different implementation. Evaluate it with the strategy signatures of comparable strategies within the JoinNode in Drools.
Historical past and naming
I created Bavet as a POC in 2019 and added it into OptaPlanner as an experimental, quick, incomplete function. There it sat frozen. For 3 years. Till just lately, when Lukáš Petrovický and me accomplished all lacking options and refactored it to the efficiency sensation is right this moment.
Naming sensible, bavet is a Flemish (Dutch) slang phrase for a bib. Very helpful in case your child is drooling. I got here up with that title after we had been consuming with our youngsters at a spaghetti restaurant known as Bavet, whereas dealing with this mural:
Okay, perhaps I didn’t put a lot effort into that.
Nevertheless it doesn’t actually need title. It’s simply one in all OptaPlanner’s rating calculation choices. An implementation element, actually.
Stability
We consider Bavet may be very steady. We efficiently run our 48+ hours stress exams on Bavet repeatedly. These stress exams stomp out rating corruption by fixing numerous datasets throughout many use instances.
Extra light-weight
In OpenShift and Kubernetes clouds, the dimensions of pods matter. Through the use of Bavet, you possibly 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 meeting of jar-with-dependencies
with solely Bavet included is 10 MB
smaller:
Core dependencies | Dimension | Discount | Core exclusions |
---|---|---|---|
All (default) | 17.5 MB | 0% | none |
Drools CS solely | 17.1 MB | -2% | optaplanner-constraint-drl , optaplanner-constraint-streams-bavet |
Bavet CS solely | 7.0 MB | -60% | optaplanner-constraint-drl , optaplanner-constraint-streams-drools |
By default, optaplanner-core
contains each Drools and Bavet, so you must 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. Particularly, all these jars are eliminated out of 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 (apart from optaplanner-constraint-streams-common
).
Attempt it out
First improve to OptaPlanner 8.27.0.Remaining
or later, should you haven’t already. In the event you’re utilizing the deprecated scoreDRL
method, migrate from scoreDRL to constraint streams first.
By default, OptaPlanner nonetheless makes use of Drools for constraint streams. To make use of Bavet as a substitute, explicitly change the ConstraintStreamImplType
to BAVET
:
Plain Java
Change to Bavet in both your *.java
file:
SolverFactory<TimeTable> solverFactory = SolverFactory.create(new SolverConfig() ... .withConstraintStreamImplType(ConstraintStreamImplType.BAVET) ...);
or in your solverConfig.xml
:
<scoreDirectorFactory> ... <constraintStreamImplType>BAVET</constraintStreamImplType> </scoreDirectorFactory>
Quarkus
Change to Bavet in src/important/sources/utility.properties
:
quarkus.optaplanner.solver.constraintStreamImplType=BAVET
Spring
Change to Bavet in src/important/sources/utility.properties
:
optaplanner.solver.constraintStreamImplType=BAVET