==> tests/block.c <==1/* mesh-ref block.rs's test, then a golden digest from the Rust: every reduction and scan (f32 sum
2 * and max, u32 and u64 sums; inclusive and exclusive) for 20 seeds at 32..1024 threads. */
3#include "mesh/ref/block.h"
4
5#include "mesh/ftz.h"
6
7#include <stdio.h>
8
9static int failures;
10#define CHECK(c) \
11 do { \
12 if (!(c)) { \
13 fprintf(stderr, "%s:%d: CHECK(%s) failed\n", __FILE__, __LINE__, #c); \
14 failures++; \
15 } \
16 } while (0)
17
18static uint64_t fnv(uint64_t h, uint64_t w) { return (h ^ w) * 0x100000001b3ull; }
19
20static void rust_trees_and_identities(void) {
21 /* The tree over 1024 leaves, whatever the warps: 4 warps == 32 warps with the rest -0.0. */
22 float v[128], w[1024];
23 uint32_t u[256];
24 uint64_t dummy;
25 float fd;
26 for (uint32_t t = 0; t < 128; t++) mesh_block_test_values(7, t, &v[t], &u[0], &dummy);
27 for (int t = 0; t < 1024; t++) w[t] = t < 128 ? v[t] : mesh_block_sum_f32_id();
28 CHECK(mesh_ftz_bits(mesh_block_reduce_f32(v, 128, mesh_block_sum_f32_id(), mesh_block_add_f32)) ==
29 mesh_ftz_bits(mesh_block_reduce_f32(w, 1024, mesh_block_sum_f32_id(), mesh_block_add_f32)));
30 /* u32 sums: any order; scans are prefix sums. */
31 for (uint32_t t = 0; t < 256; t++) mesh_block_test_values(9, t, &fd, &u[t], &dummy);
32 uint32_t seq = 0;
33 for (int t = 0; t < 256; t++) seq += u[t];
34 CHECK(mesh_block_reduce_u32(u, 256, 0, mesh_block_add_u32) == seq);
35 uint32_t inc[256], exc[256], acc = 0;
36 CHECK(mesh_block_scan_u32(u, 256, 0, mesh_block_add_u32, true, inc));
37 CHECK(mesh_block_scan_u32(u, 256, 0, mesh_block_add_u32, false, exc));
38 for (int t = 0; t < 256; t++) {
39 CHECK(exc[t] == acc);
40 acc += u[t];
41 CHECK(inc[t] == acc);
42 }
43 /* max: total order, one of the inputs. */
44 CHECK(mesh_ftz_bits(mesh_block_max_f32(-0.0f, 0.0f)) == 0);
45 CHECK(mesh_ftz_bits(mesh_block_max_f32(0.0f, -0.0f)) == 0);
46 float m[32];
47 const float pat[4] = {1.0f, -2.0f, 3.5f, mesh_ftz_from_bits(0xff800000u)};
48 for (int i = 0; i < 32; i++) m[i] = pat[i % 4];
49 CHECK(mesh_block_reduce_f32(m, 32, mesh_block_max_f32_id(), mesh_block_max_f32) == 3.5f);
50 /* Invalid sizes. */
51 CHECK(mesh_block_reduce_u32(u, 33, 7, mesh_block_add_u32) == 7);
52 CHECK(!mesh_block_scan_u32(u, 0, 0, mesh_block_add_u32, true, inc));
53}
54
55static void rust_golden(void) {
56 static const size_t ns[6] = {32, 64, 128, 256, 544, 1024};
57 static float f[1024], fo[1024];
58 static uint32_t u[1024], uo[1024];
59 static uint64_t w[1024], wo[1024];
60 uint64_t h = 0xcbf29ce484222325ull;
61 for (uint32_t seed = 0; seed < 20; seed++) {
62 for (int ni = 0; ni < 6; ni++) {
63 size_t n = ns[ni];
64 for (uint32_t t = 0; t < n; t++) mesh_block_test_values(seed, t, &f[t], &u[t], &w[t]);
65 h = fnv(h, mesh_ftz_bits(mesh_block_reduce_f32(f, n, mesh_block_sum_f32_id(), mesh_block_add_f32)));
66 h = fnv(h, mesh_ftz_bits(mesh_block_reduce_f32(f, n, mesh_block_max_f32_id(), mesh_block_max_f32)));
67 h = fnv(h, mesh_block_reduce_u32(u, n, 0, mesh_block_add_u32));
68 h = fnv(h, mesh_block_reduce_u64(w, n, 0, mesh_block_add_u64));
69 for (int inc = 1; inc >= 0; inc--) {
70 mesh_block_scan_f32(f, n, mesh_block_sum_f32_id(), mesh_block_add_f32, inc, fo);
71 for (size_t i = 0; i < n; i++) h = fnv(h, mesh_ftz_bits(fo[i]));
72 mesh_block_scan_f32(f, n, mesh_block_max_f32_id(), mesh_block_max_f32, inc, fo);
73 for (size_t i = 0; i < n; i++) h = fnv(h, mesh_ftz_bits(fo[i]));
74 mesh_block_scan_u32(u, n, 0, mesh_block_add_u32, inc, uo);
75 for (size_t i = 0; i < n; i++) h = fnv(h, uo[i]);
76 mesh_block_scan_u64(w, n, 0, mesh_block_add_u64, inc, wo);
77 for (size_t i = 0; i < n; i++) h = fnv(h, wo[i]);
78 }
79 }
80 }
81 if (h != 0xb142436d933cba7eull) {
82 fprintf(stderr, "block digest %#018llx, want 0xb142436d933cba7e\n", (unsigned long long)h);
83 failures++;
84 }
85}
86
87int main(void) {
88 rust_trees_and_identities();
89 rust_golden();
90 return failures != 0;
91}