我试图了解是什么使Drools在我们的用例中表现不佳。我在这里阅读了Phreak文档:https://access.redhat.com/documentation/en-us/red_hat_decision_manager/7.4/html/decision_engine_in_red_hat_decision_manager/phreak-algorithm-con_decision-engine
似乎没有提及有关节点到节点跳转如何完成的任何内容。这是一个解释我的意思的示例:
假设我有一个包含三个字段的Person对象:name,lastName,SSN
我以此方式定义了大量规则:当lastName ='Smith'和SSN ='XXXXXXXXXX'时,name =“ Jane”。假设我有很多人以“ Smith”作为姓氏,例如10,000,那么给定一个姓氏和一个SSN来获得一个名字的复杂性是什么?将进行10,000次比较,还是“ Smith”节点与所有基础SSN保持某种形式的哈希图?]
如果使用等式运算符代替SSN而不是SSN,我将使用一个范围(例如日期范围)来定义如下规则:“当lastName ='Smith'并且startedShool> = 2005和finishSchool <= 2010”。节点是否保留一些特殊的数据结构来加快使用范围运算符的查询速度?
编辑:每个请求我都添加一个示例来解释如何设置规则
我们有一个名为Evidence的类作为输入和输出。我们在不同的激活组中构建每个证据实例,并将其添加到集合中。我们通常定义一些通用性低的显着性规则,并添加具有更具体条件和更高显性性的规则。
这是两个规则的示例,其重要性越来越高。我们定义其中的〜10K。
人们可以想象一种树状结构,其中每个层次的显着性都增加,条件变得更加严格。 Evidence类用作一种键值对,因此它们中的许多将具有相同的相同节点(例如name = location)。
要执行规则,我们将添加两个证据(例如[name = location,stringValue ='BBB']和[name = code,stringValue = theCode])并触发规则。
rule "RULE 1::product"
salience 0
activation-group "product"
when
$outputs : EvidenceSet.Builder( )
Evidence( name == "location" && stringValue == "BBB" )
then
$outputs.addEvidences(Evidence.newBuilder().setName("Product").setStringValue("999"));
end
rule "RULE 2::product"
salience 1
activation-group "product"
when
$outputs : EvidenceSet.Builder( )
Evidence( name == "location" && stringValue == "BBB" )
Evidence( name == "code" && stringValue == "thecode" )
then
$outputs.addEvidences(Evidence.newBuilder().setName("Product").setStringValue("777"));
end
这里是规则模板
rule "RULE %s::product"
when
$outputs : EvidenceSet.Builder( )
Evidence( name == "location" && stringValue == "BBB" )
Evidence( name == "code" && stringValue matches ".*code.*" )
then
$outputs.addEvidences(Evidence.newBuilder().setName("Product").setStringValue("777"));
end
这是从模板中构建10000条规则drl的代码
public static void main(String[] args) throws IOException {
String template = Resources.toString(Resources.getResource("template.drl"), Charset.defaultCharset());
try (PrintStream file = new PrintStream(new File("test.drl"))) {
for (int i = 0; i < 10_000; i++)
file.println(String.format(template, i));
}
}
这里是基于您的drl语法的域类,不包括equals,hashCode和toString,对我来说看起来很奇怪...
public class Evidence {
public String name;
public String stringValue;
public Evidence() {
}
public Evidence(String name, String stringValue) {
this.name = name;
this.stringValue = stringValue;
}
public static Builder newBuilder() {
return new Builder();
}
}
public class EvidenceSet {
public static Set<Evidence> evidenceSet = new HashSet<>();
public static class Builder {
public Evidence evidence = new Evidence();
public Builder setName(String name) {
evidence.name = name;
return this;
}
public Builder setStringValue(String stringValue) {
evidence.stringValue = stringValue;
return this;
}
public void addEvidences(Builder builder) {
evidenceSet.add(builder.evidence);
}
}
}
这里是执行10k规则文件的测试
@DroolsSession("classpath:/test.drl")
public class PlaygroundTest {
private PerfStat firstStat = new PerfStat("first");
private PerfStat othersStat = new PerfStat("others");
@Rule
public DroolsAssert drools = new DroolsAssert();
@Test
public void testIt() {
firstStat.start();
drools.insertAndFire(new EvidenceSet.Builder(), new Evidence("location", "BBB"), new Evidence("code", "thecode"));
firstStat.stop();
for (int i = 0; i < 500; i++) {
othersStat.start();
drools.insertAndFire(new Evidence("code", "code" + i));
othersStat.stop();
}
System.out.printf("first, ms: %s%n", firstStat.getStat().getAvgTimeMs());
System.out.printf("others, ms: %s%n", othersStat.getStat().getAvgTimeMs());
System.out.println(EvidenceSet.evidenceSet);
}
}
规则和测试应符合您的要求,以匹配规则中的所有决策节点。议程组和显着性与性能无关,为简单起见,在此跳过。
没有“ for”部分的测试,给出以下分析结果
“获胜者是org.drools.core.rule.JavaDialectRuntimeData.PackageClassLoader.internalDefineClass
,它为流口水创建并注册自动生成的Java类,然后阻止,这是重量级的操作。
带有“ for”部分的测试,给出了完全不同的图片
注意internalDefineClass
保持不变,并且测试代码的执行变得显着。因为加载了1万条规则类,并且在我的12台CPU机器上花费了12,5秒。之后,所有其他插入均在avg中执行。 700毫秒让我们分析一下那个时间。
然后,规则块将在平均0.01毫秒内执行。
RULE 9::product - min: 0.00 avg: 0.01 max: 1.98 activations: 501
10000 RHS * 0.01毫秒= 100毫秒是10k条规则执行以下命令所花费的时间
$outputs.addEvidences(Evidence.newBuilder().setName("Product").setStringValue("777"));
虚拟对象的创建和操作变得很显着,因为您将其倍增到10k。根据第二次性能分析结果,大多数情况下是将测试运行器的输出打印到控制台。总计100毫秒。考虑到您粗暴地创建对象以服务于“构建者”范例,RHS的执行速度非常快。
为了回答您的问题,我会猜测并建议以下问题:
您不得重复使用已编译的规则会话。
它可能被低估了,从而阻止了执行时间,如果您在RHS中的某个地方有虚拟String.format,那么它的整体执行时间将是值得注意的,因为format相对较慢并且您要处理1万次执行。
可能存在笛卡尔规则匹配。仅仅因为您正在使用结果集,所以您只能猜测有多少“完全相同的结果”已插入到该结果集中,这意味着执行了RHS。
如果您使用的是全状态会话(出于某种原因,我猜测您没有使用它,这是问题的根本原因),我看不到任何内存清理和对象收回的操作。这可能会导致OOME出现性能问题。这是运行的内存占用...
如果以前不能解决问题,我仍然要求您提供工作示例能够重现问题的测试。工作意味着可以复制并运行。