aboutsummaryrefslogtreecommitdiffstats
path: root/tests/check.py
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-10-05 12:25:27 +0100
committereunhhu <[email protected]>2026-10-05 12:25:27 +0100
commit89f45e11fc5d6a8c6f5a556195dc2f363239568f (patch)
treeb6db59631e6fcfffdaae4e6d05dde686602330e7 /tests/check.py
parent2c0dc0ed32ab0d88f985aadbb6541591bdef5461 (diff)
downloadpure-89f45e11fc5d6a8c6f5a556195dc2f363239568f.tar.gz
pure-89f45e11fc5d6a8c6f5a556195dc2f363239568f.zip
Compile each nest of loops twice, the second with whole numbers as integers (narrow columns, counts, their arithmetic within 2^53), giving up to the executor for the rest of a round when that fails: BFS core 14.9 to 13.4 ms (022)
Diffstat (limited to 'tests/check.py')
-rw-r--r--tests/check.py62
1 files changed, 62 insertions, 0 deletions
diff --git a/tests/check.py b/tests/check.py
index d62bb91..e9f00bb 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -1016,6 +1016,67 @@ def module_ranges(aup, pure, work):
return row
+ASSUMPTIONS_SOURCE = """record Cell {
+ at = 0
+ v = 0
+}
+state total = 0
+state big = 0
+state count = 0
+input go = message {
+ n: u32
+ half: u32
+}
+on go {
+ let cells: list of Cell
+ let keyed: list of Cell by at
+ for i in 0 .. go.n {
+ cells.append(Cell { at: i, v: i * 3 })
+ keyed.add(Cell { at: i, v: i + 1 })
+ }
+ for i in 0 .. go.n * 2 {
+ if i >= cells.count { break }
+ if i == go.half {
+ cells.append(Cell { at: 0.5, v: -0.0 })
+ keyed.add(Cell { at: go.n + 0.5, v: 0.25 })
+ }
+ let c = cells.first + i
+ total = total + c.v + c.at * 2
+ if keyed.has(c.at) {
+ total = total + keyed[c.at].v
+ }
+ big = big + c.at * 1099511627776
+ count = count + cells.count
+ }
+}
+"""
+
+
+def module_assumptions(aup, pure, work):
+ """The compiled code's integers (Pure docs/design/022) where they stop
+ holding: a loop whose columns hold small whole numbers until, halfway,
+ an item with a fraction (and -0) widens them and a fraction's key takes
+ a keyed list out of its DENSE form; a product past 2^53. Each event's
+ states, compiled with every loop (PURE_JIT=all) and run by the executor
+ alone (PURE_JIT=0), are the same byte for byte."""
+ import os
+ import struct
+ source, module = work / 'assumptions.aup', work / 'assumptions.pmod'
+ source.write_text(ASSUMPTIONS_SOURCE)
+ compile_module(aup, source, module)
+ runs = [(10, 3), (10, 99), (70, 65), (70, 5), (200, 150), (9000, 4000), (9000, 9999), (3, 0)]
+ commands = ''.join(f'message go {struct.pack("<II", n, half).hex()}\n' for n, half in runs)
+ outputs = {}
+ for jit in ('all', '0'):
+ done = subprocess.run([str(pure / 'build/pure-run'), str(module)], input=commands, capture_output=True, text=True,
+ timeout=120, env=dict(os.environ, PURE_JIT=jit))
+ assert done.returncode == 0, done.stderr
+ outputs[jit] = done.stdout
+ assert outputs['all'] == outputs['0'], next((a, b) for a, b in zip(outputs['all'].splitlines(), outputs['0'].splitlines()) if a != b)
+ assert all(json.loads(line)['fault'] == 'none' for line in outputs['0'].splitlines())
+ return len(runs)
+
+
def server_links_refused(aup, pure, work):
"""A link to an input the receiver's app does not have: the receiver
says so (REFUSED), and the sender says it once on stderr, as a wire says
@@ -5074,6 +5135,7 @@ def main():
with tempfile.TemporaryDirectory(prefix='pure-check-') as directory:
work = Path(directory)
report['module_ranges'] = module_ranges(aup, pure, work)
+ report['module_assumptions'] = module_assumptions(aup, pure, work)
report['server_parity_requests'] = sum(server_parity(aup, pure, work, range(args.seeds), threads=threads)
for threads in (1, 3))
report['server_pipelined_answers'] = server_pipelining(aup, pure, work)