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

  • 2020 年 2 月 26 日
  • 筆記

上一篇介紹了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) {...}    }

原創不易,如果覺得文章對你有幫助,歡迎點贊、評論。文章有疏漏之處,歡迎批評指正。