h48

A prototype for an optimal Rubik's cube solver, work in progress.
git clone https://git.tronto.net/h48
Download | Log | Files | Refs | README | LICENSE

commit 4a88a686d5e08404080ed6954b70c0db252aa220
parent 9ac266c76f39620d8343e46ca41cb09d1534384c
Author: Sebastiano Tronto <sebastiano@tronto.net>
Date:   Sat, 14 Dec 2024 11:59:22 +0100

Allow choosing number of threads when calling solve()

Diffstat:
Mconfigure.sh | 4+++-
Mpython/example.py | 2+-
Mpython/nissy_module.c | 12+++++++-----
Mshell/shell.c | 18+++++++++++++++---
Msrc/nissy.c | 16+++++++++++++++-
Msrc/nissy.h | 4++++
Msrc/solvers/h48/solve.h | 17++++++++++-------
Mtools/300_solve_small/solve_small.c | 2+-
Mtools/301_solve_file/solve_file.c | 2+-
Mtools/302_solve_multisol/solve_multisol.c | 2+-
10 files changed, 58 insertions(+), 21 deletions(-)

diff --git a/configure.sh b/configure.sh @@ -14,7 +14,9 @@ # The string "architecture" must be one of "AVX2", "NEON" or "PORTABLE". # # THREADS=n -# Choose how many threads to use for multi-threaded oerations. +# The maximum number of threads to use for multi-threaded operations. +# This is also used as default value in case an operation allows +# specifying how many threads to use. # By default, 8 threads will be used (TODO: in the future this will be # determined base on the system). # The number n must be between 1 and 128. diff --git a/python/example.py b/python/example.py @@ -23,7 +23,7 @@ data = bytearray(open("tables/" + solver, "rb").read()) cube = nissy.applymoves(nissy.solved_cube, "U F R2"); # Solve! -solutions = nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, -1, data) +solutions = nissy.solve(cube, solver, nissy.nissflag_normal, 0, 9, 3, -1, 4, data) # Print the solutions, one per line print("Found ", len(solutions), " solutions:") diff --git a/python/nissy_module.c b/python/nissy_module.c @@ -306,7 +306,8 @@ checkdata(PyObject *self, PyObject *args) } PyDoc_STRVAR(solve_doc, -"solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions, optimal, data)\n" +"solve(cube, solver, nissflag, minmoves, maxmoves, maxsolutions," +" optimal, threads, data)\n" "--\n\n" "Solves the given 'cube' with the given 'solver' and other parameters." "See the documentation for libnissy (in nissy.h) for details.\n" @@ -319,6 +320,7 @@ PyDoc_STRVAR(solve_doc, " - maxsolution: the maximum number of solutions to return\n" " - optimal: the largest number of moves from the shortest solution" "(set to -1 to ignore)\n" +" - threads: the number of threads to use (0 for default)\n" " - data: a bytearray containing the data for the solver\n" "\n" "Returns: a list with the solutions found\n" @@ -328,20 +330,20 @@ solve(PyObject *self, PyObject *args) { long long result; unsigned nissflag, minmoves, maxmoves, maxsolutions; - int optimal, i, j, k; + int optimal, i, j, k, threads; const char *cube, *solver; char solutions[MAX_SOLUTIONS_SIZE]; long long stats[NISSY_SIZE_SOLVE_STATS]; PyByteArrayObject *data; PyObject *list, *item; - if (!PyArg_ParseTuple(args, "ssIIIIiY", &cube, &solver, &nissflag, - &minmoves, &maxmoves, &maxsolutions, &optimal, &data)) + if (!PyArg_ParseTuple(args, "ssIIIIiiY", &cube, &solver, &nissflag, + &minmoves, &maxmoves, &maxsolutions, &optimal, &threads, &data)) return NULL; Py_BEGIN_ALLOW_THREADS result = nissy_solve(cube, solver, nissflag, minmoves, maxmoves, - maxsolutions, optimal, data->ob_alloc, data->ob_bytes, + maxsolutions, optimal, threads, data->ob_alloc, data->ob_bytes, MAX_SOLUTIONS_SIZE, solutions, stats); Py_END_ALLOW_THREADS diff --git a/shell/shell.c b/shell/shell.c @@ -28,6 +28,7 @@ #define FLAG_MAXMOVES "-M" #define FLAG_OPTIMAL "-O" #define FLAG_MAXSOLUTIONS "-n" +#define FLAG_THREADS "-t" #define INFO_CUBEFORMAT(cube) cube " must be given in B32 format." #define INFO_MOVESFORMAT "The accepted moves are U, D, R, L, F and B, " \ @@ -54,6 +55,7 @@ typedef struct { unsigned maxmoves; unsigned optimal; unsigned maxsolutions; + unsigned threads; } args_t; static int64_t compose_exec(args_t *); @@ -88,6 +90,7 @@ static bool set_minmoves(int, char **, args_t *); static bool set_maxmoves(int, char **, args_t *); static bool set_optimal(int, char **, args_t *); static bool set_maxsolutions(int, char **, args_t *); +static bool set_threads(int, char **, args_t *); static bool set_id(int, char **, args_t *); static uint64_t rand64(void); @@ -113,6 +116,7 @@ struct { OPTION(FLAG_MAXMOVES, 1, set_maxmoves), OPTION(FLAG_OPTIMAL, 1, set_optimal), OPTION(FLAG_MAXSOLUTIONS, 1, set_maxsolutions), + OPTION(FLAG_THREADS, 1, set_threads), OPTION(NULL, 0, NULL) }; @@ -193,9 +197,10 @@ struct { "solve", "solve " FLAG_SOLVER " SOLVER" "[" FLAG_MINMOVES " n] [" FLAG_MAXMOVES " N] " - FLAG_CUBE " CUBE", + FLAG_CUBE " CUBE" + FLAG_THREADS " T", "Solve the given CUBE using SOLVER, " - "using at least n and at most N moves. " + "using at least n and at most N moves, and T threads. " INFO_CUBEFORMAT("CUBE"), solve_exec ), @@ -475,7 +480,7 @@ solve_exec(args_t *args) ret = nissy_solve( args->cube, args->str_solver, nissflag, args->minmoves, - args->maxmoves, args->maxsolutions, args->optimal, + args->maxmoves, args->maxsolutions, args->optimal, args->threads, size, buf, SOLUTIONS_BUFFER_SIZE, solutions, stats); free(buf); @@ -557,6 +562,7 @@ parse_args(int argc, char **argv, args_t *args) .maxmoves = 20, .optimal = -1, .maxsolutions = 1, + .threads = 0, }; if (argc == 0) { @@ -728,6 +734,12 @@ set_maxsolutions(int argc, char **argv, args_t *args) return parse_uint(argv[0], &args->maxsolutions); } +static bool +set_threads(int argc, char **argv, args_t *args) +{ + return parse_uint(argv[0], &args->threads); +} + void log_stderr(const char *str, ...) { diff --git a/src/nissy.c b/src/nissy.c @@ -489,6 +489,7 @@ nissy_solve( unsigned maxmoves, unsigned maxsols, int optimal, + int threads, unsigned long long data_size, const char data[data_size], unsigned sols_size, @@ -499,6 +500,7 @@ nissy_solve( cube_t c; long long parse_ret; uint8_t h, k; + int t; if (solver == NULL) { LOG("Error: 'solver' argument is NULL\n"); @@ -522,10 +524,22 @@ nissy_solve( return 0; } + t = threads == 0 ? THREADS : threads; + if (t < 0) { + LOG("solve: 'threads' is negative. Please provide a " + "number of threads between 1 and %d\n", THREADS); + return NISSY_ERROR_OPTIONS; + } + if (t > THREADS) { + LOG("solve: 'threads' is above the maximum value of %d\n", + THREADS); + return NISSY_ERROR_OPTIONS; + } + if (!strncmp(solver, "h48", 3)) { parse_ret = parse_h48_solver(solver, &h, &k); if (parse_ret == NISSY_OK) - return solve_h48(c, minmoves, maxmoves, maxsols, + return solve_h48(c, minmoves, maxmoves, maxsols, t, data_size, data, sols_size, sols, stats); else return parse_ret; diff --git a/src/nissy.h b/src/nissy.h @@ -259,6 +259,9 @@ Parameters: maxsols - The maximum number of solutions. optimal - If set to a non-negative value, the maximum number of moves above the optimal solution length. + threads - The number of threads to use. Must be less than or equalt to + the value of the compile-time constant THREADS. If set to 0, + the default value THREADS will be used. data_size - The size of the data buffer. data - The data for the solver. Can be computed with gendata. sols_size - The size of the solutions buffer. @@ -286,6 +289,7 @@ nissy_solve( unsigned maxmoves, unsigned maxsolutions, int optimal, + int threads, unsigned long long data_size, const char data[data_size], unsigned sols_size, diff --git a/src/solvers/h48/solve.h b/src/solvers/h48/solve.h @@ -38,6 +38,7 @@ typedef struct { int64_t nodes_visited; int64_t table_fallbacks; int64_t table_lookups; + int threads; int ntasks; solve_h48_task_t *tasks; int thread_id; @@ -62,7 +63,7 @@ STATIC int64_t solve_h48_maketasks( solve_h48_task_t [static STARTING_CUBES], int *); STATIC void *solve_h48_runthread(void *); STATIC int64_t solve_h48_dfs(dfsarg_solve_h48_t *); -STATIC int64_t solve_h48(cube_t, int8_t, int8_t, uint64_t, uint64_t, +STATIC int64_t solve_h48(cube_t, int8_t, int8_t, uint64_t, int, uint64_t, const void *, uint64_t, char *, long long [static NISSY_SIZE_SOLVE_STATS]); STATIC int64_t @@ -316,7 +317,7 @@ solve_h48_runthread(void *arg) dfsarg = (dfsarg_solve_h48_t *)arg; cube = dfsarg->start_cube; - for (i = dfsarg->thread_id; i < dfsarg->ntasks; i += THREADS) { + for (i = dfsarg->thread_id; i < dfsarg->ntasks; i += dfsarg->threads) { task = dfsarg->tasks[i]; memcpy(dfsarg->moves, task.moves, STARTING_MOVES); dfsarg->cube = cube; @@ -407,6 +408,7 @@ solve_h48( int8_t minmoves, int8_t maxmoves, uint64_t maxsolutions, + int threads, uint64_t data_size, const void *data, uint64_t solutions_size, @@ -461,7 +463,7 @@ solve_h48( fallback2 = h48data + offset; symmask = symmetry_mask(cube); - for (i = 0; i < THREADS; i++) { + for (i = 0; i < threads; i++) { arg[i] = (dfsarg_solve_h48_t) { .start_cube = cube, .cube = cube, @@ -481,6 +483,7 @@ solve_h48( .nodes_visited = 0, .table_fallbacks = 0, .table_lookups = 0, + .threads = threads, .thread_id = i, .solutions_mutex = &solutions_mutex, }; @@ -505,7 +508,7 @@ solve_h48( if (*arg[0].nsols >= (int64_t)maxsolutions) goto solve_h48_done; - for (i = 0; i < THREADS; i++) { + for (i = 0; i < threads; i++) { arg[i].ntasks = ntasks; arg[i].tasks = tasks; } @@ -520,12 +523,12 @@ solve_h48( if (d >= 10) LOG("Found %" PRId64 " solutions, searching at depth %" PRId8 "\n", nsols, d); - for (i = 0; i < THREADS; i++) { + for (i = 0; i < threads; i++) { arg[i].depth = d; pthread_create( &thread[i], NULL, solve_h48_runthread, &arg[i]); } - for (i = 0; i < THREADS; i++) + for (i = 0; i < threads; i++) pthread_join(thread[i], NULL); } @@ -534,7 +537,7 @@ solve_h48_done: goto solve_h48_error_solutions_buffer; nodes_visited = table_lookups = table_fallbacks = 0; - for (i = 0; i < THREADS; i++) { + for (i = 0; i < threads; i++) { nodes_visited += arg[i].nodes_visited; table_fallbacks += arg[i].table_fallbacks; table_lookups += arg[i].table_lookups; diff --git a/tools/300_solve_small/solve_small.c b/tools/300_solve_small/solve_small.c @@ -37,7 +37,7 @@ void run(void) { continue; } n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL, - 0, 20, 1, -1, size, buf, SOL_BUFFER_LEN, sol, stats); + 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats); if (n == 0) printf("No solution found\n"); else diff --git a/tools/301_solve_file/solve_file.c b/tools/301_solve_file/solve_file.c @@ -24,7 +24,7 @@ void run(void) { continue; } nsols = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL, - 0, 20, 1, -1, size, buf, SOL_BUFFER_LEN, sol, stats); + 0, 20, 1, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats); if (nsols == 0) printf("No solution found\n"); else diff --git a/tools/302_solve_multisol/solve_multisol.c b/tools/302_solve_multisol/solve_multisol.c @@ -30,7 +30,7 @@ void run(void) { continue; } n = nissy_solve(cube, solver, NISSY_NISSFLAG_NORMAL, - 0, 20, nsol, -1, size, buf, SOL_BUFFER_LEN, sol, stats); + 0, 20, nsol, -1, 0, size, buf, SOL_BUFFER_LEN, sol, stats); if (n == 0) printf("No solution found\n"); else