From 74f1fb63e11d7030de1013a634435ed4ee11d056 Mon Sep 17 00:00:00 2001 From: Sacha Ligthert Date: Mon, 27 Jan 2025 00:13:00 +0100 Subject: [PATCH] Have the ability to do partial workloads. (Read: Split searches among multiple machines.) --- solver/flags.go | 24 +++++++++++++++++++ solver/solver.go | 6 +++++ solver/split.go | 61 ++++++++++++++++++++++++++++++++++++++++++++++++ solver/types.go | 2 ++ 4 files changed, 93 insertions(+) create mode 100644 solver/split.go diff --git a/solver/flags.go b/solver/flags.go index e5d0ce0..b0056ba 100644 --- a/solver/flags.go +++ b/solver/flags.go @@ -20,6 +20,8 @@ func (solver *Solver) parse_flags() { var row7 string var row8 string var row9 string + var split int + var part int // Define parameters flag.StringVar(&row1, "row1", "000000000", "1st row of the sudoku puzzle.") @@ -32,10 +34,13 @@ func (solver *Solver) parse_flags() { flag.StringVar(&row8, "row8", "000000000", "8th row of the sudoku puzzle.") flag.StringVar(&row9, "row9", "000000000", "9th row of the sudoku puzzle.") flag.IntVar(&solver.numcpus, "numcpu", runtime.NumCPU(), "Number of CPU cores to assign to this task.") + flag.IntVar(&split, "split", 1, "Split the tasks in n parts. This depends on the availability of the first row.") + flag.IntVar(&part, "part", 1, "Process part x in n parts. Cannot be lower than 1, or higher than specified in split.") // Parse the flags flag.Parse() + // Process any changes to the CPU usage. if solver.numcpus <= 0 { log.Printf("ERROR: Number of CPU cores must be 1 or higher.\n\n") solver.print_Usage() @@ -46,6 +51,7 @@ func (solver *Solver) parse_flags() { runtime.GOMAXPROCS(solver.numcpus) } + // Process rows if row1 == "000000000" || row2 == "000000000" || row3 == "000000000" || row4 == "000000000" || row5 == "000000000" || row6 == "000000000" || row7 == "000000000" || row8 == "000000000" || row9 == "000000000" { log.Printf("ERROR: All parameters must be entered.\n\n") solver.print_Usage() @@ -74,6 +80,24 @@ func (solver *Solver) parse_flags() { solver.row8 = row8 solver.row9 = row9 + // Process workload splitting + // Ensure split and part are 1 or higher + if split <= 0 || part <= 0 { + log.Printf("ERROR: '-split' and '-part' need to be 1 or higher.\n") + solver.print_Usage() + os.Exit(1) + } + + // Ensure part is between 1 and split + if part > split { + log.Printf("ERROR: '-part' cannot be bigger than `-split`.\n") + solver.print_Usage() + os.Exit(1) + } + + solver.split = split + solver.part = part + } func (solver *Solver) validate_row(name string, row string) { diff --git a/solver/solver.go b/solver/solver.go index bd05c94..262938b 100644 --- a/solver/solver.go +++ b/solver/solver.go @@ -24,6 +24,12 @@ func Run() { // Find rows that fit with the entered rows solver.populate_blocks() + // If needed, split the workload + // May exit and throw an error if the work load isn't viable + if solver.split != 1 { + solver.select_workload() + } + // Print the total number of solutions to validate log.Println("Number of (potential) solutions:", solver.iter) diff --git a/solver/split.go b/solver/split.go new file mode 100644 index 0000000..34d145f --- /dev/null +++ b/solver/split.go @@ -0,0 +1,61 @@ +package solver + +import ( + "log" + "os" + "strconv" + "time" +) + +// Perform some checks +// and +// Modify solver.row1s so it limits the workload to what is only desired. +func (solver *Solver) select_workload() { + if solver.split > len(solver.row1s) { + log.Println("ERROR: Unable to divide the workload in " + strconv.Itoa(solver.split) + " parts, when only " + strconv.Itoa(len(solver.row1s)) + " are available.\n\n") + os.Exit(1) + } + defer solver.timeTrack(time.Now(), "Workload set") + log.Println("Setting workload") + log.Println("We are agent " + strconv.Itoa(solver.part) + " of " + strconv.Itoa(solver.split)) + workloads := solver.split_workload() + solver.set_workload(workloads) +} + +// Determine how workload should be split among the agents +func (solver *Solver) split_workload() []int { + agents := make([]int, solver.split) + var tracker int + var tasks int = len(solver.row1s) + + for tasks != 0 { + agents[tracker] += 1 + tasks -= 1 + tracker += 1 + if tracker == solver.split { + tracker = 0 + } + } + + return agents +} + +// Set the workload by setting solver.row1s +func (solver *Solver) set_workload(agents []int) { + var start int = 0 + var finish int = 0 + for key, value := range agents { + if key == solver.part-1 { + finish = start + value + break + } else { + start += value + } + } + + // Set the shortened set of instructions + solver.row1s = solver.row1s[start:finish] + + // Recalculate how much we need to grind through + solver.iter = int64(len(solver.row1s)) * int64(len(solver.row2s)) * int64(len(solver.row3s)) * int64(len(solver.row4s)) * int64(len(solver.row5s)) * int64(len(solver.row6s)) * int64(len(solver.row7s)) * int64(len(solver.row8s)) * int64(len(solver.row9s)) +} diff --git a/solver/types.go b/solver/types.go index a9ffa00..8f77d26 100644 --- a/solver/types.go +++ b/solver/types.go @@ -29,4 +29,6 @@ type Solver struct { solutions []string rates []int64 numcpus int + split int + part int }