我想要实现的目标
目前我在我的OptaPlanner项目中运行大量输入,并且目前正在实施约束,他们花了很长时间来计算初始分数。因此,给定的求解器会破坏整个基准测试,因为它会卡住而无法终止。作为分数计算类型,我正在使用Drools。
我试图提前终止一个解算器,在一段时间后仍然没有通过初始分数计算(没有"解决开始"显示)。因此,在单个基准测试中,我想运行多个不同的输入,并且对于每个输入我希望有一个给定的计时器,如果该计时器在初始分数计算完成之前到期,我希望求解器立即终止。一个理想的选择是获得完成分数计算的百分比。
我之所以不仅仅是为了进行优化,是因为我希望有一个比较基线,并在优化过程中跟踪结果。因此,初始分数计算的多少百分比对我来说至关重要。
我目前所掌握的知识
solver.terminateEarly()
方法。Map<Integer,Solver> solverMap
中进行查找,其中键是执行求解器的线程的hashCode的值 - &gt; Thread.currentThread().hashCode()
。随着求解器的开始和结束,这个Map正在更新。这样我就可以从所有地方进行查找(optaplanner-examples,optaplanner-core,optaplanner-benchmark项目和Drools规则(以下示例))kcontext.getKieRuntime().halt()
,用于立即终止规则执行。kcontext.getKieRuntime().halt()
。例如:在下面的规则中,在ShiftAssignment实例中的每次更改后将到达then部分,如果解算器设置为提前终止,则将停止规则执行。
salience 1 //so that it is triggered first
rule "ShiftAssignmentChange"
when
ShiftAssignment()
then
if(TerminateBenchmarkEarly.solverMap.get(Thread.currentThread().hashCode()).isTerminateEarly()){
kcontext.getKieRuntime().halt();//This command is used to terminate the fire loop early.
}
end
这些规则的意图是他们salience 1
反对默认选项0,因此它们将是第一个将被执行的规则并且规则执行将立即停止
6.来自kieSession.fireAllRules()
方法的org.optaplanner.core.impl.score.director.drools.DroolsScoreDirector calculateScore
调用返回已执行的规则数。我可以使用这个度量作为初始分数达到多少的基线。随着优化的进行,预计这个数字会越来越高,所花费的时间越来越短。
我目前面临的问题
我遇到的问题是即使再次执行此操作也需要花费大量时间来完成规则中的检查,或者在某些情况下由于OutOfMemory错误导致崩溃。打开Drools的Trace选项,我能够看到将事实插入工作内存的一小部分时间,然后它不断输出TRACE BetaNode stagedInsertWasEmpty=false
。问题在于来自kieSession.fireAllRules()
方法的org.optaplanner.core.impl.score.director.drools.DroolsScoreDirector calculateScore
调用,fireAllRules
的代码来自Drools核心,此代码被编译为JAR,因此无法进行编辑。
结论
无论如何,我知道这是一个黑客,但正如我上面所说,我需要这些信息作为基线,以了解我当前的解决方案在哪里,并随着优化继续跟踪基准信息。 如果有不同的(更聪明的)方式可以实现这一点,我很乐意这样做。
基准测试结果
输入1
- 实体数:12,870
- 可变数:7,515
- 最大值数:21
- 问题量表:22,068
- 加载inputSolution后(创建解算器之前)的内存使用情况:平均44,830,840字节。
- 施工启发后的平均得分计算速度= 1965 /秒
- 本地搜索后的平均得分计算速度= 1165 /秒
- 求解器完成后的平均得分计算速度= 1177 /秒
输入2
- 实体数:17,559
- 可变数:7,515
- 最大值数:8
- 问题量表:21,474
- 加载inputSolution后(创建解算器之前)的内存使用情况:平均为5,964,200字节。
- 施工启发后的平均得分计算速度= 1048 /秒
- 本地搜索后的平均得分计算速度= 1075 /秒
- 求解器完成后的平均得分计算速度= 1075 /秒
输入3
- 实体数:34,311
- 可变数:14,751
- 最大值数:8
- 问题量表:43,458
- 加载inputSolution后的内存使用情况(创建解算器之前):平均为43,478,536字节。
- 施工启发后的平均得分计算速度= 1134 /秒
- 本地搜索后的平均得分计算速度= 450 /秒
- 求解器完成后的平均得分计算速度= 452 /秒
输入4
- 实体数:175,590
- 可变数:75,150
- 最大值数:11
- 问题规模:240,390
- 加载inputSolution后的内存使用率(创建解算器之前):平均为36,089,240字节。
- 施工启发后的平均得分计算速度= 739 /秒
- 本地搜索后的平均得分计算速度= 115 /秒
- 求解器完成后的平均得分计算速度= 123 /秒
输入5
- 实体数:231,000
- 可变数:91,800
- 最大值数:31
- 问题量表:360,150
- 加载inputSolution后的内存使用率(创建解算器之前):平均136,651,744字节。
- 施工启发后的平均得分计算速度= 142 /秒
- 本地搜索后的平均得分计算速度= 11 /秒
- 解算器完成后的平均得分计算速度= 26 /秒
输入6
- 实体数:770,000
- 可变数:306,000&#39;
- 最大值计数: 51
- 问题规模:1,370,500
- 加载后的内存使用情况 inputSolution(在创建解算器之前):114,488,056字节 平均。
- 施工后的平均得分计算速度 启发式= 33 /秒
- 本地搜索后的平均得分计算速度= 1 /秒
- 求解器完成后的平均得分计算速度= 17 /秒
在评论Drools中的规则时,我获得下一个平均分数 计算速度(输入6):
- 施工启发后= 17800 /秒
- 本地搜索后= 22557 /秒
- 解算器完成后= 21690 /秒
答案 0 :(得分:1)
如果可能的话,我首先关注的是让DRL更快,而不是这些黑客。所以这归结为找出哪些得分规则很慢。使用得分计算速度(在上一个INFO日志行中),通过评论得分规则并查看其对得分计算速度的影响来确定。
话虽如此,通常我会建议您查看unimprovedSecondsSpentLimit
或自定义Termination
- 但这确实无济于事,因为初始分数是根据划痕计算的:它们仅在每次移动之间进行检查(因此在每fireAllRules()
之间进行检查,通常为10k / sec)。