Releases: dds-bridge/dds
Release list
v3.1.0
DDS 3.1.0 builds on the modernised 3.0 core. The headline changes for anyone
consuming the solver are new .NET and Java bindings, a much larger
Python surface, batch solving that is parallel by default, and a set of
double-dummy correctness fixes. Existing 3.0 code continues to work unchanged.
Highlights
New language bindings
- .NET (
DDS_Core) — a type-safe .NET 8+ wrapper covering both the legacy C
API (SolveBoard,CalcDDtable,Par,AnalysePlay, …) and the modern
SolverContextAPI. One AnyCPU assembly runs on macOS, Linux, and Windows;
the native library is located automatically or viaDDS_LIBRARY_PATH.
See dotnet_interface.md. - Java (FFM / Project Panama) — call DDS from JDK 22+ with no hand-written
JNI glue.//jni:dds_sharedproduces one self-contained native library per
OS, packaged with the native binary inside the jar.
See jni_interface.md. - A pure-C ABI shim (
dds_c_api.h) underpins both. It takes only pointers
and plain-old-data, so FFM, P/Invoke, andctypesall bind against the same
stable surface instead of each inventing its own.
Python
dds3 now exposes the rest of the solver, not just the single-board entry
points:
analyse_play_pbn,analyse_all_plays_pbn— trick-by-trick play analysis.solve_all_boards_pbn,solve_all_boards_bin— batch solving, parallelised
across hardware threads inside the library.dealer_par— par contracts from the dealer's perspective.initialize_static_memory(mirrors the C-sideSetMaxThreads→
InitializeStaticMemorydeprecation).
All of these release the GIL around the native call and validate their inputs.
Batch entry points accept an optional max_threads.
Performance
- Batch work is parallel inside the library.
SolveAllBoards*and
CalcAllTables*share one work-stealing dispatcher backed by a persistent
worker pool, each worker holding its own reusableSolverContext. Hardest
boards are dispatched first to shorten the tail. - Search efficiency is back at 2.9 parity. 3.0 carried move-ordering
regressions from the refactor — signed/unsigned truncation in the heuristic
and quick-tricks paths, and a trump-void ordering bug — that made the search
explore materially more nodes than 2.9. Those are fixed; results are
unchanged, the search just does less work to reach them. - Warm solver context across calls. Reusing a
SolverContextbetween
related solves keeps its per-thread search state and its transposition table
alive. The TT survives as long as the next call is on the same or a similar
deal with the same trump; a genuinely new deal or a new trump resets it, as in
2.9. This is now the documented pattern and is used internally by the batch
paths.clear_tt()remains the way to discard the table explicitly — it
disposes the instance and keeps only the configuration, which rebuilds an
empty table on next use. - Thin LTO on macOS builds, inlined hot accessors, and native WASM exception
handling. - New performance tooling — utilities for comparing solver performance
between two commits, and for recording warm benchmarks from a live consumer.
The comparison below (18-core Mac) shows solver performance is back at 2.9
levels:
Summary (avg user ms)
==============================================================================
solver file dtest2.9 dtest3.0 develop
------ ------------- ------------ ------------ ------------
solve list100.txt 2.18 29.40 2.59
solve list10.txt 23.76 52.60 24.60
solve list1.txt 8.37 15.00 8.00
calc list100.txt 7.61 124.47 6.67
calc list10.txt 22.40 108.30 21.30
calc list1.txt 53.39 189.00 55.00
------ ------------- ------------ ------------ ------------
TOTAL solve 4.18 31.36 4.62
TOTAL calc 9.35 123.59 8.42
Correctness fixes
AnalysePlayunder-counted tricks (#156): each card was analysed against
a cold transposition table, so the hint-bounded search settled on the wrong
bound. The play path now reuses the caller's context, matchingSolveBoard.- Move ordering and pruning corruption from signed→unsigned casts (see
above) — search behaviour only, but it cost significant time. - Heap-use-after-free in
clear_ttand a null dereference in
TransTableS::reset_memoryafter memory release. - Par output now names the declaring seat when successive par contracts
differ, in both the C++ and .NET paths. - Worker exceptions in parallel board solving are reported as
RETURN_UNKNOWN_FAULT
rather than terminating the process.
New public C API
Thread-count-aware and sequential variants were added alongside the existing
entry points, so callers can size or opt out of the library's parallelism:
CalcDDtableN, CalcDDtablePBNN, CalcAllTablesN, CalcAllTablesPBNN,
CalcAllTablesX, CalcAllTablesPBNX, SolveAllBoardsN, SolveAllBoardsBinN,
SolveAllBoardsSeq, SolveAllBoardsBinSeq.
SetMaxThreads is deprecated in favour of InitializeStaticMemory; the old
name still works and no longer influences batch parallelism.
Platforms and build
- Windows/MSVC is a first-class target again: Visual Studio project files
undersolution/, plus Windows CI for the native build and the .NET bindings. - WebAssembly builds hermetically via Bazel's Emscripten toolchain — no
manualemsdkinstall — with multithreading, a heap-budgeted worker cap, and
a browser demo (web/) that runs the solver entirely client-side. A live
build is deployed to https://dds-bridge.github.io/dds/. - CI covers Linux, macOS, Windows, and WASM. Linux runs ASan, TSan, UBSan,
and MSan; macOS runs ASan, TSan, and UBSan over//library/tests/system/...;
Windows and WASM run no sanitizers. - Use
bazelisk; the Bazel version is pinned in.bazelversionand the
committedMODULE.bazel.lockexpects it.
Tooling
//benchmarks:dds_replayreplays a recorded real-world DDS workload
(154,370 solved deals), timing it and verifying every answer — a benchmark and
a regression test in one.//benchmarks:warm_tt_benchmarkisolates the
transposition-table reuse win.dd_table_for_dealCLI in Python, C++, and .NET;create_list_for_dtestfor
generating hand lists; regenerated, de-duplicatedhands/lists.- Capability specs under
specs/document the public API, bindings, threading,
and build system.
Compatibility
No API breaks for 3.0 consumers. The only deprecation is SetMaxThreads,
which remains available as an alias.
SolveAllBoards*/CalcAllTables* now spawn threads by default where 3.0 was
sequential; pass maxThreads = 1 to the *N/*X variants, or call
SolveAllBoardsSeq/SolveAllBoardsBinSeq, to stay on one thread. The
incorrect results with v3 #156
fix changes the trick counts returned by AnalysePlay*; they were previously
under-counted.
Contributors
@BonyJordan, @BSalita, @ed2k, @jdh8, @mortensp, @tameware, @ThorvaldAagaard,
@tzimnoch, @wopdevries, @zzcgumn
V3.0.0
v.2.9.0: Moving out of beta
With some minor fixes, we are now turning v2.9.0 beta into v2.9.0.
v2.9.0-beta: Please provide feedback
Under the hood there are large changes vs. v2.8.4:
- The multi-threading support has been generalized and rewritten. It is now possible for a single DLL or library to contain many multi-threading implementations which can be selected dynamically via a new interface function. The user can compile with the desired options by configuring the Makefile. Currently supported options include Windows API, OpenMP, GDC, Boost, STL (both managed by DDS and managed by the host system), TBB, and PPL (managed by the host system).
- The Makefiles have become fewer as a result. The source and include files are now centralized in text files in the Makefiles directory that are loaded by each Makefile.
- The DLL or library now supports both the "large" and the "small" transition-table (TT) options dynamically. The threads with "small" TTs will run in a lot less memory, but a bit less slowly than the "large" ones. DDS will choose the best combination for you depending on the available memory, including mixtures.
If you use this beta version successfully, please let us know. We plan to turn it into a real release once we get enough feedback.
Binaries are only on Bo's website for now, http://privat.bahnhof.se/wb758135/.
Maintenance release
Added a lower-memory DLL version with a compile flag (SMALL_MEMORY_OPTION). This is about 11-14% slower, but uses a lot less memory. For example, for a single thread on one PC, the memory usage was 24 MB rather than 110 MB. For 12 threads, the numbers were 89 MB rather than 1.1 GB. You have to link against the right DLL for you -- you cannot make a given DLL behave in both ways. Get binaries from the DDS website, http://privat.bahnhof.se/wb758135/.
Added TDC 32-bit and 64-bit Makefiles contributed by Sergio Polini.
Made the SolveAllChunks functions aliases to SolveAllBoards.
v2.8.3: Maintenance update
Fixed bug in Scheduler that could cause a crash in case more than 128 very similar hands were solved in one invocation. If you didn't experience crashes, you don't have to upgrade for this bug fix.
Generalized the AnalysePlay functions. Previously they required a complete 13-trick hand with no cards played in a partial trick. Now they work for any number of remaining tricks and for partial tricks as well.
Included Pierre Cossard's code for using GDC multi-threading with Apple's LLVM compiler on the Mac.
Added a Makefile for compiling a shared library on the Mac.
Added a GetDDSInfo interface function that can be used to ask the DLL how it was compiled and how many threads it is using.
Modified the documentation names not to include the version number in the file names (easier to maintain).
Updated the documentation to fix an error (the playTracePBN structure uses an array of chars, not an array of ints; this was correct in dll.h). Also described pass as a valid par score.
v2.8.2
This is mainly a bug fix of the par contract calculation in v2.8.1 in certain rare cases. The par score calculation appears to have been correct. (The same fix was applied retroactively to v2.5.2 to create v2.5.3.)
In addition, a typo in one of the examples was fixed (CalcAllTablesPBN.cpp). And a comment was added to dll.h to explain parResultsDealer better.
Retroactive fix of par contract calculation
Around v2.8.1. it was discovered that the par contract calculation in some earlier releases was not always correct or complete. This affected about 0.4% of the cases. The par value calculation seems correct.
Since v2.5.2 was an important release and some users rely on it, we decide to publish a retroactive fix. This is a "dead end" for the code development, though, so please consider moving to later versions if you can. As of this writing, the most recent version is v2.8.1.
Formal release of v2.8.1
Fixed bug in initialization that affected some cases when initial moves were given.
Fixed bug that affected solutions of hands where only one trick was remaining at the time of the call.
Fixed bug in scheduler that caused some hands to be considered identical that weren't.
Added Makefiles kindly contributed by users for Linux shared libraries (as opposed to static libraries) and for Microsoft's make version.
Added Foppe Hemminga's DLL documentation written entirely in markup (.md) language.
v2.8.0
Introduction
v2.8.0 is the first DDS release that was developed on git and was not retro-fitted into git. Thanks to Brian Dickens (@hostilefork) for his tireless help!
Soren Hein officially became a co-author as of this release and is the person responsible day-to-day at the moment.
Contributions are welcome. Please follow this branching model. In particular, use branches for units of change (new features etc); don't merge them into your own develop branch but submit them; and then sync your develop to the DDS develop when it advances.
Speed
DDS v2.8.0 is about 15% faster than DDS v2.7.0 on one benchmark. Because of new functions and because of a dependence on input data, it is becoming increasingly difficult to quantify the speed with a single number.
Interface
- No changes to any of the bridge functions.
- SetMaxThreads() does not have to be called on Windows. On some Linux systems it is not necessary, but we recommend that you call it explicitly on all non-Windows systems to be on the safe side. You can use an argument of 0 for auto-configuration.
- ErrorMessage() was added for convenience. If you get a numerical error message from DDS, you can call this function to get a text message.
Functions and modes
- We now recommend that you use SolveAllBoards() and not SolveAllChunksBin(), SolveAllChunks() or SolveAllChunksBin(). See below for an explanation.
- The mode parameter in SolveBoard(), SolveBoardPBN() and in the boards and boardsPBN structs remains available, and DDS does broadly do what it used to do with this parameter. However, the transposition tables are now deemed an internal implementation issue that the user should not have to worry about. The mode parameter may stop working or may do something different in the future. It is recommended always to set it to 0. If you think you need it, please let us know why.
- CalcAllTables() now can be called with up to 32 DD tables in one batch, rather than 10. Please always use the largest batch size that you have available, for this and other functions.
Platforms
DDS now comes with Makefiles for six systems:
- The Microsoft Visual C++ compiler (we believe the 2010 and 2013 versions).
- The mingw compiler (on Windows).
- The cygwin g++ compiler (on Windows).
- The g++ compiler on Linux.
- The clang compiler on the Mac.
- The g++ compiler on the Mac.
The code has provisions for multi-threading using:
- The Windows API.
- OpenMP.
It can also be compiled single-threaded.
The Makefiles are set to have a very high level of warnings and to fail on warnings and not just on errors. This is easy enough to switch off, but we'd prefer if you let us know what needs to be fixed in the code to pass your system.
Binaries
An unofficial DLL for Windows is attached, compiled with the Microsoft Visual C++ compiler version 18.00.21005.1. The official and probably absolute fastest DLL is distributed on Bo Haglund's website.
Distribution structure
Please see the README.md file in the top directory. DDS is now distributed with a test program (including input files and expected results), a set of simple example programs for all major interface functions, and a documentation directory.
DDD is not a direct part of the distribution. DDD has not been updated for many years, but as part of this version, DDD was updated minimally in order to compile on the same six platforms as above (Makefiles supplied).
Internals
The move generation code has been re-implemented (as an object) and is much faster.
A scheduler object has been added which attempts to run the hands in a batch in an order that minimizes execution time. This requires predicting which hands are going to be hard, which turns out to be a difficult problem. We've made a start, and ideas are welcome.
The scheduler detects duplicate hands anywhere within a batch, and not just if they are perfectly aligned in groups as required by the chunk functions. Therefore there is little reason to use the chunk functions anymore.
The code for initialization and the SolveBoard() interface functions have been restructured. This led to a particular speed-up for the AnalysePlay function family which is now very fast indeed.
Bugs were fixed.