aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastiano Tronto <sebastiano@tronto.net>2025-12-17 17:36:16 +0100
committerSebastiano Tronto <sebastiano@tronto.net>2025-12-17 17:36:16 +0100
commitec9593e2ff0856d78b37df3a977a753be82bfddc (patch)
tree684dad42207f48125db40ce0afb32046cdba1726
parentac3a91f4f173e7a38c70d5cd0caf5a025642312b (diff)
downloadnissy-core-ec9593e2ff0856d78b37df3a977a753be82bfddc.tar.gz
nissy-core-ec9593e2ff0856d78b37df3a977a753be82bfddc.zip
Fix alignment
Diffstat (limited to '')
-rw-r--r--benchmarks/benchmarks.md5
-rw-r--r--doc/h48.md12
-rw-r--r--doc/solvers.md9
-rw-r--r--shell/shell.c11
-rw-r--r--src/nissy.h12
-rw-r--r--tools/tool.h11
6 files changed, 53 insertions, 7 deletions
diff --git a/benchmarks/benchmarks.md b/benchmarks/benchmarks.md
index adc3377..ddcb91f 100644
--- a/benchmarks/benchmarks.md
+++ b/benchmarks/benchmarks.md
@@ -247,3 +247,8 @@ Time per cube adjusted for table size (in seconds \* GiB, lower is better).
247* For H48, both GCC and Clang have been tried, with the same options; 247* For H48, both GCC and Clang have been tried, with the same options;
248 the resulting executable was about 10% faster with GCC compared to Clang. 248 the resulting executable was about 10% faster with GCC compared to Clang.
249 vcube only supports compiling with Clang. 249 vcube only supports compiling with Clang.
250* The performance of the H48 solver depends slightly, but measurably, on the
251 alignment of the pruning table in memory. This is not handled by the core
252 library, but by the program that uses it. For these tests, we have used as
253 reference implementation the program in `tools/301_solve_file`, which
254 ensures 64-byte alignment.
diff --git a/doc/h48.md b/doc/h48.md
index 2487aad..d9ef192 100644
--- a/doc/h48.md
+++ b/doc/h48.md
@@ -225,6 +225,8 @@ of 1, 2 or 3 can be used directly as a lower bound of b+1, b+2 and b+3
225respectively. However, a value of 0 could mean that the actual lower 225respectively. However, a value of 0 could mean that the actual lower
226bound is anything between 0 and b, so we cannot take b as a lower bound. 226bound is anything between 0 and b, so we cannot take b as a lower bound.
227 227
228#### Fallback tables
229
228To be able to still use some sort of pruning value even when we get a 230To be able to still use some sort of pruning value even when we get a
2290 read, we use a **fallback table**. Inspired by nxopt, this table is 2310 read, we use a **fallback table**. Inspired by nxopt, this table is
230interleaved with the main table for cache efficiency: every 254 entries 232interleaved with the main table for cache efficiency: every 254 entries
@@ -235,6 +237,16 @@ when looking up the minimum value in the fallback table after a 0 read.
235Smaller lines (of 256 or 128 bits) have been tried, but they do not 237Smaller lines (of 256 or 128 bits) have been tried, but they do not
236give any significant improvement over 512 bit lines. 238give any significant improvement over 512 bit lines.
237 239
240This trick provides the gratest performance benefits if the main pruning
241table is properly aligned. Unfortunately, as a design choice, the
242solver is implemented here as a library that defer all memory allocation
243business to the implementor. We do make sure that the table is properly
244aligned in the programs provided in this repository (for example, the
245rudimentary shell and the tools), but we do not enforce this in the
246main library code.
247
248#### Additional (fast) lookups
249
238Moreover, as an additional heuristic, in case of a 0 read we also look 250Moreover, as an additional heuristic, in case of a 0 read we also look
239up another pruning value in a table that takes into account only the 251up another pruning value in a table that takes into account only the
240position of the edges. This table is small (around 1MB), so repeated 252position of the edges. This table is small (around 1MB), so repeated
diff --git a/doc/solvers.md b/doc/solvers.md
index a246c54..837cec6 100644
--- a/doc/solvers.md
+++ b/doc/solvers.md
@@ -16,6 +16,15 @@ about how this solver works, see [h48.md](./h48.md). For benchmarks see
16* Moveset: HTM (all 18 basic moves). 16* Moveset: HTM (all 18 basic moves).
17* From 115MB to 59GB (roughly 2<sup>X</sup>*56MB). 17* From 115MB to 59GB (roughly 2<sup>X</sup>*56MB).
18 18
19*Note: for better performance, the solver's data should be 64-byte
20aligned. To achieve this, one can use
21[`aligned_alloc(64, size)`](https://en.cppreference.com/w/c/memory/aligned_alloc)
22in C11 or later,
23[`_aligned_malloc(size, 64)`](https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/aligned-malloc)
24on Windows platforms, or the
25[aligned `new` operator](https://cppreference.com/w/cpp/memory/new/operator_new.html)
26in C++17 or later.*
27
19## Coordinate solvers 28## Coordinate solvers
20 29
21Various solvers to solve different substeps, commonly used for Fewest 30Various solvers to solve different substeps, commonly used for Fewest
diff --git a/shell/shell.c b/shell/shell.c
index 0e624c9..698252d 100644
--- a/shell/shell.c
+++ b/shell/shell.c
@@ -14,6 +14,13 @@
14#define SOLUTIONS_BUFFER_SIZE UINT64_C(500000) 14#define SOLUTIONS_BUFFER_SIZE UINT64_C(500000)
15#define MAX_PATH_LENGTH UINT64_C(10000) 15#define MAX_PATH_LENGTH UINT64_C(10000)
16 16
17#if defined(_WIN32)
18#define wrap_aligned_alloc(align, size) malloc(size)
19#else
20#define wrap_aligned_alloc(align, size) \
21 (((size) % (align) == 0) ? aligned_alloc((align), (size)) : malloc(size))
22#endif
23
17#define FLAG_CUBE "-cube" 24#define FLAG_CUBE "-cube"
18#define FLAG_COMMAND "-command" 25#define FLAG_COMMAND "-command"
19#define FLAG_STR_CUBE "-cubestr" 26#define FLAG_STR_CUBE "-cubestr"
@@ -348,7 +355,7 @@ gendata_exec(args_t *args)
348 return -2; 355 return -2;
349 } 356 }
350 357
351 buf = malloc(size); 358 buf = wrap_aligned_alloc((size_t)64, size);
352 359
353 ret = nissy_gendata(args->str_solver, size, buf); 360 ret = nissy_gendata(args->str_solver, size, buf);
354 if (ret < 0) { 361 if (ret < 0) {
@@ -448,7 +455,7 @@ solve_exec(args_t *args)
448 if (args->maxsolutions == 0) 455 if (args->maxsolutions == 0)
449 args->maxsolutions = args->optimal == 20 ? 1 : UINT_MAX; 456 args->maxsolutions = args->optimal == 20 ? 1 : UINT_MAX;
450 457
451 buf = malloc(size); 458 buf = wrap_aligned_alloc((size_t)64, size);
452 read = fread(buf, size, 1, file); 459 read = fread(buf, size, 1, file);
453 fclose(file); 460 fclose(file);
454 if (read != 1) { 461 if (read != 1) {
diff --git a/src/nissy.h b/src/nissy.h
index b5abc15..c85072a 100644
--- a/src/nissy.h
+++ b/src/nissy.h
@@ -304,7 +304,8 @@ Parameters:
304 data_size - The size of the data buffer. It is advised to use 304 data_size - The size of the data buffer. It is advised to use
305 nissy_solverinfo to check how much memory is needed. 305 nissy_solverinfo to check how much memory is needed.
306 data - The return parameter for the generated data. 306 data - The return parameter for the generated data.
307 This buffer must have 8-byte alignment. 307 This buffer must have 8-byte alignment. Some solvers (such as
308 h48) will perform better if the buffer is 64-byte aligned.
308 309
309Return values: 310Return values:
310 NISSY_ERROR_INVALID_SOLVER - The given solver is not known. 311 NISSY_ERROR_INVALID_SOLVER - The given solver is not known.
@@ -328,7 +329,10 @@ Parameters:
328 solver - The name of the solver. 329 solver - The name of the solver.
329 data_size - The size of the data buffer. 330 data_size - The size of the data buffer.
330 data - The data for the solver. Can be computed with gendata. 331 data - The data for the solver. Can be computed with gendata.
331 This buffer must have 8-byte alignment. 332 This buffer must have 8-byte alignment. Some solvers (such as
333 h48) will perform better if the buffer is 64-byte aligned, but
334 this is not relevant for the purpose of checking the integrity
335 of the data.
332 336
333Return values: 337Return values:
334 NISSY_OK - The data is valid. 338 NISSY_OK - The data is valid.
@@ -358,7 +362,9 @@ Parameters:
358 to 0, the default value THREADS will be used. 362 to 0, the default value THREADS will be used.
359 data_size - The size of the data buffer. 363 data_size - The size of the data buffer.
360 data - The data for the solver. Can be computed with gendata. 364 data - The data for the solver. Can be computed with gendata.
361 This buffer must have 8-byte alignment. 365 This buffer must have 8-byte alignment. Some solvers
366 (such as h48) will perform better if the buffer is
367 64-byte aligned.
362 sols_size - The size of the solutions buffer. 368 sols_size - The size of the solutions buffer.
363 sols - The return parameter for the solutions. The solutions are 369 sols - The return parameter for the solutions. The solutions are
364 separated by a '\n' (newline) and a '\0' (NULL character) 370 separated by a '\n' (newline) and a '\0' (NULL character)
diff --git a/tools/tool.h b/tools/tool.h
index c6fafbc..96e7f27 100644
--- a/tools/tool.h
+++ b/tools/tool.h
@@ -8,6 +8,13 @@
8 8
9#include "../src/nissy.h" 9#include "../src/nissy.h"
10 10
11#if defined(_WIN32)
12#define wrap_aligned_alloc(align, size) malloc(size)
13#else
14#define wrap_aligned_alloc(align, size) \
15 (((size) % (align) == 0) ? aligned_alloc((align), (size)) : malloc(size))
16#endif
17
11static void log_stderr(const char *, void *); 18static void log_stderr(const char *, void *);
12static double timerun(void (*)(void)); 19static double timerun(void (*)(void));
13static void writetable(const unsigned char *, int64_t, const char *); 20static void writetable(const unsigned char *, int64_t, const char *);
@@ -117,7 +124,7 @@ generatetable(
117 return -1; 124 return -1;
118 } 125 }
119 126
120 *buf = malloc(size); 127 *buf = wrap_aligned_alloc((size_t)64, size);
121 gensize = nissy_gendata(solver, size, *buf); 128 gensize = nissy_gendata(solver, size, *buf);
122 129
123 if (gensize != size) { 130 if (gensize != size) {
@@ -156,7 +163,7 @@ getdata(
156 } else { 163 } else {
157 printf("Reading tables from file %s\n", filename); 164 printf("Reading tables from file %s\n", filename);
158 size = nissy_solverinfo(solver, dataid); 165 size = nissy_solverinfo(solver, dataid);
159 *buf = malloc(size); 166 *buf = wrap_aligned_alloc((size_t)64, size);
160 sizeread = fread(*buf, size, 1, f); 167 sizeread = fread(*buf, size, 1, f);
161 fclose(f); 168 fclose(f);
162 if (sizeread != 1) { 169 if (sizeread != 1) {

Generated with cgit - Back to sebastiano.tronto.net