phase4_prep: exercise the permutation-identity guard (perm_sha) through save_cursor/load_cursor
Browse files- kernels/phase4_prep.py +11 -7
kernels/phase4_prep.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
| 1 |
# Phase 4 rehearsal on a CPU instance: every check a session can fail, run where failing costs no quota.
|
| 2 |
#
|
| 3 |
-
# The first GPU session of the run bills ~
|
| 4 |
-
# shape of bug that only shows up mid-run
|
| 5 |
-
#
|
| 6 |
-
#
|
| 7 |
-
#
|
| 8 |
-
#
|
| 9 |
#
|
| 10 |
# It ends with the same PLAN_JSON the GPU session will print, so the numbers can be compared before and
|
| 11 |
# after rather than trusting that the code did not change in between.
|
|
@@ -173,16 +173,20 @@ assert legs[-1][1] == steps and [l[0] for l in legs[1:]] == [l[1] for l in legs[
|
|
| 173 |
# dataset from load_cursor() rather than a hand-computed offset is the point -- the derivation from
|
| 174 |
# step to samples_consumed is part of what is being tested.
|
| 175 |
full = SD.make_dataset(store, SEQ, 20260919, start_sample=0)
|
|
|
|
|
|
|
| 176 |
for boundary in sorted({S.CKPT_EVERY, legs[len(legs) // 2][0], steps - S.CKPT_EVERY}):
|
| 177 |
d = "/tmp/cur_%d" % boundary
|
| 178 |
os.makedirs(d, exist_ok=True)
|
| 179 |
SD.save_cursor(d, SD.cursor_dict(SEQ, boundary * seqs_per_step, 20260919, fingerprint,
|
| 180 |
-
step=boundary))
|
| 181 |
c = SD.load_cursor(d)
|
| 182 |
assert c["step"] * seqs_per_step == c["samples_consumed"], c
|
| 183 |
assert c["dataset_files_sha"] == fingerprint, c
|
|
|
|
| 184 |
s0 = c["samples_consumed"]
|
| 185 |
resumed = SD.make_dataset(store, SEQ, c["shuffle_seed"], start_sample=s0)
|
|
|
|
| 186 |
assert len(resumed) == len(full) - s0, (len(resumed), len(full), s0)
|
| 187 |
same = True
|
| 188 |
for j in (0, 1, len(resumed) // 2, len(resumed) - 1):
|
|
|
|
| 1 |
# Phase 4 rehearsal on a CPU instance: every check a session can fail, run where failing costs no quota.
|
| 2 |
#
|
| 3 |
+
# The first GPU session of the run bills ~6.5 hours before it is provably healthy, and E-031/E-034 are both
|
| 4 |
+
# exactly the shape of bug that only shows up mid-run or at the very first launch. So this kernel does, on
|
| 5 |
+
# free CPU: fetches the pinned code and asserts its sha256s, resolves credentials, reads the checkpoint
|
| 6 |
+
# pointer, downloads the PUBLISHED mix from the Hub, hashes every shard against the manifest, rehearses the
|
| 7 |
+
# session planner across the whole schedule, and proves resume-equivalence of the reader at the boundaries
|
| 8 |
+
# the run actually crosses.
|
| 9 |
#
|
| 10 |
# It ends with the same PLAN_JSON the GPU session will print, so the numbers can be compared before and
|
| 11 |
# after rather than trusting that the code did not change in between.
|
|
|
|
| 173 |
# dataset from load_cursor() rather than a hand-computed offset is the point -- the derivation from
|
| 174 |
# step to samples_consumed is part of what is being tested.
|
| 175 |
full = SD.make_dataset(store, SEQ, 20260919, start_sample=0)
|
| 176 |
+
order = SD.perm_sha(n_samples, 20260919)
|
| 177 |
+
assert full.order_sha == order, "two derivations of the order identity disagree"
|
| 178 |
for boundary in sorted({S.CKPT_EVERY, legs[len(legs) // 2][0], steps - S.CKPT_EVERY}):
|
| 179 |
d = "/tmp/cur_%d" % boundary
|
| 180 |
os.makedirs(d, exist_ok=True)
|
| 181 |
SD.save_cursor(d, SD.cursor_dict(SEQ, boundary * seqs_per_step, 20260919, fingerprint,
|
| 182 |
+
step=boundary, order_sha=order))
|
| 183 |
c = SD.load_cursor(d)
|
| 184 |
assert c["step"] * seqs_per_step == c["samples_consumed"], c
|
| 185 |
assert c["dataset_files_sha"] == fingerprint, c
|
| 186 |
+
assert c["shuffle_perm_sha"] == order, c
|
| 187 |
s0 = c["samples_consumed"]
|
| 188 |
resumed = SD.make_dataset(store, SEQ, c["shuffle_seed"], start_sample=s0)
|
| 189 |
+
assert resumed.order_sha == order, "a resume lost the visit order"
|
| 190 |
assert len(resumed) == len(full) - s0, (len(resumed), len(full), s0)
|
| 191 |
same = True
|
| 192 |
for j in (0, 1, len(resumed) // 2, len(resumed) - 1):
|