Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
ca_checkpoint_phase17_example.cc
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11*/
12
40#include <charconv>
41#include <chrono>
42#include <cstddef>
43#include <cstdint>
44#include <filesystem>
45#include <iomanip>
46#include <iostream>
47#include <limits>
48#include <random>
49#include <string>
50#include <string_view>
51#include <system_error>
52#include <type_traits>
53#include <vector>
54
55#include <ca-checkpoint.H>
56#include <ca-traits.H>
57#include <tpl_ca_engine.H>
58#include <tpl_ca_lattice.H>
59#include <tpl_ca_neighborhood.H>
60#include <tpl_ca_rule.H>
61#include <tpl_ca_storage.H>
62
63using namespace Aleph;
64using namespace Aleph::CA;
65
66namespace
67{
68
71
72bool parse_u64(const char *text, std::uint64_t &out) noexcept
73{
74 if (text == nullptr)
75 return false;
76 std::string_view sv{text};
77 if (sv.empty() or sv.front() == '+' or sv.front() == '-')
78 return false;
79 std::uint64_t v = 0;
80 const auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), v, 10);
81 if (ec != std::errc{} or ptr != sv.data() + sv.size())
82 return false;
83 out = v;
84 return true;
85}
86
87Grid seed_grid(const ca_size_t side, const std::uint64_t seed, const double p_alive)
88{
89 Grid g({side, side}, 0);
90 std::mt19937_64 rng(seed);
91 std::uniform_real_distribution<double> u(0.0, 1.0);
92 for (ca_size_t i = 0; i < side; ++i)
93 for (ca_size_t j = 0; j < side; ++j)
94 g.set({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)},
95 u(rng) < p_alive ? 1 : 0);
96 return g;
97}
98
99bool grids_equal(const Grid &a, const Grid &b)
100{
101 if (a.extents() != b.extents())
102 return false;
103 for (ca_size_t i = 0; i < a.size(0); ++i)
104 for (ca_size_t j = 0; j < a.size(1); ++j)
105 if (a.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)})
106 != b.at({static_cast<ca_index_t>(i), static_cast<ca_index_t>(j)}))
107 return false;
108 return true;
109}
110
111std::size_t byte_count(const std::filesystem::path &p)
112{
113 std::error_code ec;
114 const auto sz = std::filesystem::file_size(p, ec);
115 return ec ? 0 : static_cast<std::size_t>(sz);
116}
117
118std::filesystem::path make_scratch_dir(const std::string &tag)
119{
120 const auto tick = std::chrono::steady_clock::now().time_since_epoch().count();
121 auto p = std::filesystem::temp_directory_path()
122 / ("aleph_ca_ckpt_p17_" + tag + "_"
123 + std::to_string(static_cast<long long>(tick)));
124 std::filesystem::create_directories(p);
125 return p;
126}
127
128void print_section(const std::string &title)
129{
130 std::cout << "\n=== " << title << " ===\n";
131}
132
133// ---------------------------------------------------------------------
134// Demo 1 — DEFLATE compression ratio
135// ---------------------------------------------------------------------
136
138{
139 print_section("compress");
140 const auto dir = make_scratch_dir("compress");
141
142 // Sparse Game of Life initial state — DEFLATE catches the long runs
143 // of zero bytes and shrinks the file substantially.
144 Engine eng(seed_grid(side, /*seed=*/0xC0FFEE, /*p_alive=*/0.05),
146 Moore<2, 1>{});
147
148 const auto raw_path = dir / "snap.raw.bin";
149 const auto cmp_path = dir / "snap.cmp.bin";
150
152
154 opts.compress = true;
155 opts.level = 6;
157
158 const auto raw_size = byte_count(raw_path);
159 const auto cmp_size = byte_count(cmp_path);
160 const double ratio
161 = (cmp_size > 0) ? static_cast<double>(raw_size) / static_cast<double>(cmp_size)
162 : 0.0;
163
164 std::cout << std::fixed << std::setprecision(2);
165 std::cout << " side : " << side << " x " << side
166 << " (sparse, ~5% alive)\n";
167 std::cout << " raw file : " << raw_size << " bytes ("
168 << raw_path.filename().string() << ")\n";
169 std::cout << " compressed file : " << cmp_size << " bytes ("
170 << cmp_path.filename().string() << ")\n";
171 std::cout << " compression ratio: " << ratio << "x\n";
172
173 // Verify round-trip equivalence: both files restore identically.
178
179 if (not grids_equal(restored_raw.frame(), restored_cmp.frame()))
180 {
181 std::cerr << " FAIL: raw and compressed restores differ\n";
182 std::filesystem::remove_all(dir);
183 return 1;
184 }
185 std::cout << " round-trip : PASS (raw == compressed)\n";
186
187 std::filesystem::remove_all(dir);
188 return 0;
189}
190
191// ---------------------------------------------------------------------
192// Demo 2 — Async writer overhead measurement
193// ---------------------------------------------------------------------
194
195double run_steps(Engine &eng, const std::size_t steps)
196{
197 const auto t0 = std::chrono::steady_clock::now();
198 eng.run(steps);
199 const auto t1 = std::chrono::steady_clock::now();
200 return std::chrono::duration<double, std::milli>(t1 - t0).count();
201}
202
203int demo_async(const ca_size_t side, const std::size_t steps)
204{
205 print_section("async");
206 const auto dir = make_scratch_dir("async");
207
208 // Configure a moderate snapshot cadence (every 16 steps) and a
209 // queue capacity that comfortably absorbs the bursts.
210 constexpr std::size_t every = 16;
211 constexpr std::size_t queue_capacity = 8;
212
213 // ----- Baseline run (no observer) -----
214 Engine baseline(seed_grid(side, 0x9ABC, 0.35),
216 Moore<2, 1>{});
217 const double t_baseline = run_steps(baseline, steps);
218
219 // ----- Sync observer (writes block the step loop) -----
220 Engine sync_eng(seed_grid(side, 0x9ABC, 0.35),
222 Moore<2, 1>{});
224 every,
225 (dir / "sync_{step}.bin").string());
226 sync_eng.on_post_step([&](std::size_t s, const Grid &f) { sync_obs.on_step_end(s, f); });
227 const double t_sync = run_steps(sync_eng, steps);
228
229 // ----- Async observer (writes go to a worker thread) -----
230 Engine async_eng(seed_grid(side, 0x9ABC, 0.35),
232 Moore<2, 1>{});
236 every,
237 (dir / "async_{step}.bin").string(),
238 writer);
239 async_eng.on_post_step([&](std::size_t s, const Grid &f) { async_obs.on_step_end(s, f); });
240 const double t_async = run_steps(async_eng, steps);
241 writer.flush();
242
243 // ----- Report -----
244 auto pct = [&](double t)
245 { return (t_baseline > 0.0) ? 100.0 * (t - t_baseline) / t_baseline : 0.0; };
246
247 std::cout << std::fixed << std::setprecision(2);
248 std::cout << " side : " << side << " x " << side << "\n";
249 std::cout << " steps : " << steps << "\n";
250 std::cout << " snapshot every : " << every << " steps\n";
251 std::cout << " queue capacity : " << queue_capacity << " (Block policy)\n";
252 std::cout << " baseline (no obs.) : " << t_baseline << " ms\n";
253 std::cout << " sync observer : " << t_sync << " ms ("
254 << std::showpos << pct(t_sync) << std::noshowpos << "% vs baseline)\n";
255 std::cout << " async observer : " << t_async << " ms ("
256 << std::showpos << pct(t_async) << std::noshowpos << "% vs baseline)\n";
257 std::cout << " async total written : " << writer.total_written() << "\n";
258 std::cout << " async total dropped : " << writer.total_dropped() << "\n";
259
260 if (not grids_equal(baseline.frame(), sync_eng.frame())
261 or not grids_equal(baseline.frame(), async_eng.frame()))
262 {
263 std::cerr << " FAIL: observers altered the engine trajectory\n";
264 std::filesystem::remove_all(dir);
265 return 1;
266 }
267 std::cout << " trajectory check : PASS (all three engines agree)\n";
268
269 std::filesystem::remove_all(dir);
270 return 0;
271}
272
273// ---------------------------------------------------------------------
274// Demo 3 — Delta snapshot round-trip
275// ---------------------------------------------------------------------
276
277int demo_delta(const ca_size_t side, const std::size_t steps)
278{
279 print_section("delta");
280 const auto dir = make_scratch_dir("delta");
281 const auto base_path = dir / "baseline.bin";
282 const auto delta_path = dir / "delta.bin";
283
284 // Run the engine up to a baseline step, snapshot it (raw), capture
285 // its byte payload, then advance further and store the diff.
286 Engine eng(seed_grid(side, 0xDA17A, 0.4),
288 Moore<2, 1>{});
289 const std::size_t baseline_steps = steps / 2;
290 const std::size_t delta_steps = steps - baseline_steps;
291
292 eng.run(baseline_steps);
295 const auto base_step = static_cast<std::uint64_t>(eng.steps_run());
296
297 eng.run(delta_steps);
299 opts.compress = true;
301
302 const auto base_size = byte_count(base_path);
303 const auto delta_size = byte_count(delta_path);
304 const auto dh = inspect_checkpoint(delta_path);
305
306 std::cout << std::fixed << std::setprecision(2);
307 std::cout << " side : " << side << " x " << side << "\n";
308 std::cout << " baseline step : " << base_step << "\n";
309 std::cout << " delta step : " << dh.step_count
310 << " (base_step + " << delta_steps << ")\n";
311 std::cout << " baseline file : " << base_size << " bytes\n";
312 std::cout << " delta file : " << delta_size << " bytes ("
313 << (base_size > 0
314 ? 100.0 * static_cast<double>(delta_size) / static_cast<double>(base_size)
315 : 0.0)
316 << "% of baseline)\n";
317
318 // Rebuild from baseline + delta and compare to the live engine.
319 Engine reconstructed(Grid({side, side}, 0),
321 Moore<2, 1>{});
324
325 if (reconstructed.steps_run() != eng.steps_run())
326 {
327 std::cerr << " FAIL: reconstructed step count "
328 << reconstructed.steps_run() << " != live " << eng.steps_run() << "\n";
329 std::filesystem::remove_all(dir);
330 return 1;
331 }
332 if (not grids_equal(reconstructed.frame(), eng.frame()))
333 {
334 std::cerr << " FAIL: reconstructed frame differs from live engine\n";
335 std::filesystem::remove_all(dir);
336 return 1;
337 }
338 std::cout << " reconstruction : PASS (baseline + delta == live)\n";
339
340 std::filesystem::remove_all(dir);
341 return 0;
342}
343
344} // namespace
345
346int main(int argc, char **argv)
347{
348 std::string mode = "all";
349 ca_size_t side = 128;
350 std::size_t steps = 200;
351
352 if (argc >= 2)
353 mode = argv[1];
354 if (argc >= 3)
355 {
356 std::uint64_t v = 0;
357 if (not parse_u64(argv[2], v)
358 or v > static_cast<std::uint64_t>(std::numeric_limits<ca_size_t>::max()))
359 {
360 std::cerr << "Invalid [side] (got '" << argv[2] << "')\n";
361 return 1;
362 }
363 side = static_cast<ca_size_t>(v);
364 }
365 if (argc >= 4)
366 {
367 std::uint64_t v = 0;
368 if (not parse_u64(argv[3], v)
369 or v > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max()))
370 {
371 std::cerr << "Invalid [steps] (got '" << argv[3] << "')\n";
372 return 1;
373 }
374 steps = static_cast<std::size_t>(v);
375 }
376 if (side < 8 or steps < 4)
377 {
378 std::cerr << "Invalid arguments: side >= 8, steps >= 4\n";
379 return 1;
380 }
381
382 std::cout << "ca_checkpoint_phase17_example\n";
383 std::cout << " mode = " << mode << "\n";
384 std::cout << " side = " << side << "\n";
385 std::cout << " steps = " << steps << "\n";
386
387 int rc = 0;
388 if (mode == "compress" or mode == "all")
390 if (mode == "async" or mode == "all")
391 rc |= demo_async(side, steps);
392 if (mode == "delta" or mode == "all")
393 rc |= demo_delta(side, steps);
394
395 if (mode != "compress" and mode != "async" and mode != "delta" and mode != "all")
396 {
397 std::cerr << "\nUnknown mode '" << mode
398 << "' (expected: compress | async | delta | all)\n";
399 return 1;
400 }
401
402 std::cout << "\n" << (rc == 0 ? "Overall: PASS" : "Overall: FAIL") << "\n";
403 return rc;
404}
int main()
void print_section(const string &title)
size_t steps
Definition ca-c-api.h:126
size_t size_t int32_t * out
Definition ca-c-api.h:120
Binary checkpoint format for Aleph::CA engines (Phase 15 + Phase 17 crash-safe / compress / async wri...
Common typedefs and tag types for the Cellular Automata module.
Background writer pumping snapshots onto a dedicated thread.
Lattice that adds boundary-aware access on top of a storage.
Moore (Chebyshev) neighborhood of radius R in N dimensions.
Observer that auto-saves the engine state every every steps to a templated path.
Synchronous double-buffered engine.
static mt19937 rng
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
std::vector< std::uint8_t > snapshot_frame_bytes(const Engine &engine)
Flatten an engine frame into a row-major byte buffer.
constexpr Game_Of_Life_Rule make_game_of_life_rule() noexcept
Build the canonical Game of Life rule.
Checkpoint_Header inspect_checkpoint(const std::filesystem::path &path)
Read just the header from a checkpoint file.
Resume_Token apply_delta_checkpoint(Engine &engine, const std::filesystem::path &path)
Apply a delta checkpoint to the engine's current state.
void save_delta_checkpoint(Engine &engine, const std::vector< std::uint8_t > &baseline, const std::uint64_t base_step, const std::filesystem::path &path, const Checkpoint_Options &options={})
Write a delta checkpoint relative to a previous payload.
std::size_t ca_size_t
Unsigned size component used for extents and counts.
Definition ca-traits.H:63
Resume_Token load_checkpoint_into(Engine &engine, const std::filesystem::path &path)
Restore an engine's state in-place from a checkpoint file.
void save_checkpoint(Engine &engine, const std::filesystem::path &path, const Checkpoint_Options &options={})
Write a complete engine snapshot to disk atomically.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
auto mode(const Container &data) -> std::decay_t< decltype(*std::begin(data))>
Compute the mode (most frequent value).
Definition stat_utils.H:447
User-tunable knobs for save_checkpoint.
bool compress
Enable DEFLATE compression of the frame payload.
The lattice wraps around on every axis.
Definition ca-traits.H:124
ValueArg< size_t > seed
Definition testHash.C:53
Synchronous double-buffered engine for cellular automata.
Cellular automata lattice with pluggable boundary policies.
Neighborhoods catalogue for Aleph::CA.
Rule mechanisms for Aleph::CA.
Dense, contiguous storage for cellular automata cells (1D/2D/3D).