OptaPlanner 7.32.0.Final版本彩蛋 - SolverManager之批量求解

上一篇介绍了OptaPlanner 7.32.0.Final版本中的SolverManager接口可以实现异步求解功能。本篇将继续介绍SolverManager的另一大特性 - 批量求解。

适用场景

在日常的规划系统中,求解一个问题,绝大多数情况下,容许运行的时间较有限,特别是在实时性较高的场景中,可让引擎运算时间不多。因此,这种情况下,会在启动了规划运算后,稍等片刻,即需要从求解程序中获取结果。但有些情况下,当我们遇到问题规模较大时,引擎无法在较短时间内找到相对最优解;甚至某些情况下,没有足够长的运行时间,可行解都可能无法找到。至于原因,可以参考我前面关于OptaPlanner入门文章中关于NPC, NP-Hard问题规模的说明。

因此,在一些规模大、时间要求不高的场景下,我们可以让引擎在空余时间自动运算。例如通过定时作业的方式,在非工作时间(例如晚间、节假日等)启动引擎对大多个规模的问题进行规划运算;第二天上班的时候,就可获得运算结果。在发布SolverManager之前,我们也完全可以针对不同的场景,设计相应的定时作业程序,让引擎运算自动运行。当然,这种方法与异步规划的问题一样,需要一定的系统设计经验才能把架构设计完美。而7.32.0.Final版本提供的SolverManager接口,则提供一种更简便的方法来实现些需求。

SolverManager批量规划特性

详细一下SolverManager接口,你应该会发现,与Solver对象的solve方法不同,使用SolverManager的sovle方法对一个问题进行求解时,除了把问题对象作为参数传入solve方法外,还需要传入一个problemID作为参数。其实对于这个参数很好理解,因为SolverManager可以同时对多个问题进行解,必须存在一种方法来识别不同的问题,规划完成后,调用方也需要通过指定的识别号,来获取相应的方案。SolverManager同时对多个问题进行求解时,对问题个数有何要求?它的求解行为是怎么样的呢?

SolverManager批量求解的行为

可同时求解多少个问题?

其实SolverManager不仅可以实现批量求解问题,而且可以实现同时对多 问题并行求解。通过设置SolverManager的parallelSolverCount属性,可以设置引擎在批量运算时,可以并行求解的问题数。即当SolverManger启动求解运算时,会将parallelSolverCount设定的数量的问题载入运算空间并行求解。通常parallelSolverCount值可以根据CPU、内存等计算机资源的情况,及问题的复杂度而推算。若无法判断此数量,也可以将parallelSolverCount设置为AUTO,引擎会根据具体情况,自动确定并行运算问题的数量,通常情况下,并行数是CPU核心数量的一半。

值得注意的是,此处的多个问题并行运算,与之前的求解过程多线程运算(Multithreaded incremental solving)是两个概念。多线程并行运算,指的是引擎在求解一个问题时,会将不同的可能解的子集分成多个线程同时计算(主要是计算约束分数和执行启发式算法)。而SolverManager的批量求解过程中,parallelSolverCount属性设定的,是引擎在面对多个问题时,同时求解的问题个数。大家可以设想,如果把Multithreaded incremental solving也启动起来,令引擎在对一个问题求解时可使用多个CPU核心,同时对多个问题并行求解。这种情况涉及的问题就没那么简单。因此,除非你对问题的复杂程度,CPU的核心和运算能力非常清晰,否则上述两个功能,还是设置为自动更好,引擎会根据计算资源运算时的工况,动态地自动确定并行求解的问题个数,及每个问题求解过程中应启动的线程个数。经历过单CPU多线程编程的朋友应该知道,多线程可以提高资源利用率,但有时候线程数量控制得不好,性能上却是起反效果的,最简单的是CPU的线程切换会消耗一定资源的。

批量求解的作用

在一些不太需要实时规划,规划求解不需要太频繁,运算需时较长的情况,批量求解就可以发挥较好作用。例如需要做一些季度或年度的大型供应链计划,因规划实休数量较大,问题空间可能非常巨大,需要花费相当长的时间才能得行相对最优解,甚至只能是可行解。可通过批量求解的方式,让引擎在空余时间(例如晚上、非工作日)进行运算,从而提高服务器资源的利用率。

基本用法

以下例子是OptaPlanner用户指南的例子,大家先作参考,目前还没有时间去研究SolverManager在示例程序中的代码,暂时也不知道官方示例中是否已经有SolverManager相关代码;若没有,稍后的时候我自己试用一下这些功能,再写一篇东西出来分享给大家。

public class TimeTableService {

    private SolverManager<TimeTable, Long> solverManager;

    // Returns immediately, call it for every dataset
    public void solveBatch(Long timeTableId) {
        solverManager.solve(timeTableId,
                // Called once, when solving starts
                this.findById,
                // Called once, when solving ends
                this.save);
    }

    public TimeTable findById(Long timeTableId) {...}

    public void save(TimeTable timeTable) {...}

}

原创不易,如果觉得文章对你有帮助,欢迎点赞、评论。文章有疏漏之处,欢迎批评指正。

本系列文章在公众号不定时连载,请关注公众号(让APS成为可能)及时接收,二维码:

http://weixin.qq.com/r/WjtFXSvE2HanrW8_925I (二维码自动识别)

如需了解更多关于OptaPlanner的应用,请发电邮致: kentbill@gmail.com

或到讨论组发表你的意见: https://groups.google.com/forum/#!forum/optaplanner-cn

若有需要可添加本人微信(13631823503)或QQ(12977379)实时沟通,但因本人日常工作繁忙,通过微信,QQ等工具可能无法深入沟通,较复杂的问题,建议以邮件或讨论组方式提出。(讨论组属于google邮件列表,国内网络可能较难访问,需自行解决)

我来评几句
登录后评论

已发表评论数()

相关站点

热门文章