File size: 14,757 Bytes
aa60529
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
#!/usr/bin/env bash
# =============================================================================
# CONTINUE the 64k b5 extension on 8x H100 -- to a FULL b5 epoch.
#
# PARENT: ext64k run3 step 695 (the 64k model: 67/65/52/29 at 8k/16k/32k/64k).
#   Recovered from strong-room-grows-fin-03 and now durable on HF at
#   checkpoints/ext64k_b5_rank0_step_695.ckpt (byte-verified).
#
# WHY cp=8 AND NOT cp=4: at 64k, cp=4 puts 16,384 tokens on each rank, which
#   MEASURED 108,493 MB on the H200 run. An H100 has 81,559 MiB. It does not
#   fit. cp=8 halves that to 8,192 tok/rank. There is no other lever: CP_B is
#   already 1 and bluscriptCP.cpp has no activation-recompute flag.
#
# WHY CP_INIT_FROM (weights only) AND NOT CP_CKPT_RESUME: ZeRO-1 shards
#   optimizer state across the GLOBAL process group (bluscriptCP.cpp:24-28), so
#   a sidecar written at world_size=4 cannot load at world_size=8 --
#   ShardedOptimizer::load_state throws ZeroCheckpointError("cross-world-size
#   resume not supported"). Weights-only is what every prior stage in this
#   project used across a topology change, including the 64k run itself.
#   TWO CONSEQUENCES, both deliberate:
#     1. the data loader restarts at token 0, so the first 42% of b5 is seen a
#        second time and the remaining 58% for the first time -- a full pass;
#     2. Adam m/v reset, which CP_REWARMUP (100 steps to 1.8e-4) absorbs.
#
# WHY THE FULL EPOCH IS ONLY NOW POSSIBLE: yesterday's box had 5 b5 train
#   shards (0.481 B tokens). All 9 are here now = 0.866 B = a real epoch.
#
# TAIL=100, NOT 2: yesterday's 2-step tail left the final LR at 1.2e-4, not the
#   ~6e-5 floor -- the checkpoint was labelled decayed but effectively was not.
#
# UPLOAD IS VERIFIED BY BYTE SIZE. Yesterday's up() helper was
#   `[ -e "$1" ] || return 0`, which silently SUCCEEDS when the source is
#   missing. That is how the step-695 checkpoint was lost: the CLI never
#   errored, the log said nothing, and 17 GB never left the box.
# =============================================================================
set -uo pipefail
cd "$(dirname "$0")"
ROOT="$(cd ../.. && pwd)"
export LD_LIBRARY_PATH="$ROOT/Tensor-Implementations/lib:$ROOT/Profiler/lib:${LD_LIBRARY_PATH:-}"
export OMPI_ALLOW_RUN_AS_ROOT=1 OMPI_ALLOW_RUN_AS_ROOT_CONFIRM=1
export PATH=/usr/local/cuda/bin:$PATH

PY=/root/gv/envv/bin/python3          # this box's venv (NOT /root/envv)
HF=/root/gv/envv/bin/hf
EXE=./build/bluscriptCP_exec
CK=checkpoints_bluscriptcp/rank_0
LOG=cont64k_logs; mkdir -p "$LOG"
M="$LOG/master.log"
REPO=unparallelled/BluScriptCP

T=65536; NP=8; GPUS=0,1,2,3,4,5,6,7
ORIG=32768; SCALE=2                   # 2 * 32768 == 65536, the parent's own cache
DATA=Data_Loader/Data/b5_65536_131072
PARENT="$CK/blumodelcp_run3_step_695.ckpt"
HAY="$PWD/Data_Loader/Data/b2_8192_16384/val_shard_00000.bin"
WARM=100; TAIL=${TAIL:-100}; FREQ=${FREQ:-100}; KEEP=2
GB=524288

say(){ echo "[$(date -u +%H:%M:%S)] $*" | tee -a "$M" >&2; }
die(){ say "FATAL: $*"; exit 1; }

# ---- preflight -------------------------------------------------------------
$PY -c "import tiktoken" 2>/dev/null || die "tiktoken missing (eval would silently score nothing)"
[ -x "$EXE" ]    || die "$EXE not built"
# CP_ATTN_FUSION=split needs BOTH kernel families compiled in: CP_FUSED_ROPE
# (Stage 2 QK-norm+RoPE) and CP_ATTN_SPLIT (Stage 3 causal attention). Without
# them SplitAttnSDPA_cp.h compiles to stubs that THROW at the first forward --
# a default `make` produced exactly that and the run died 73 s in. The Makefile
# records the flag state in this stamp; refuse to start if it is not right.
STAMP=build/objects/.cp_rope_flags
[ -f "$STAMP" ] || die "no build-flag stamp: rebuild with make CP_FUSED_ROPE=1 CP_ATTN_SPLIT=1 SM_ARCH=90 bluscript-cp"
grep -q 'CP_FUSED_ROPE=1' "$STAMP" && grep -q 'CP_ATTN_SPLIT=1' "$STAMP" || \
  die "binary built WITHOUT the split kernels ($(cat "$STAMP")). Rebuild: make CP_FUSED_ROPE=1 CP_ATTN_SPLIT=1 SM_ARCH=90 bluscript-cp -j\$(nproc)"
[ "$EXE" -nt "$STAMP" ] || say "WARN: $EXE is older than the flag stamp -- rebuild if unsure"
[ -f "$PARENT" ] || die "parent missing: $PARENT"
[ -f "$HAY" ]    || die "haystack missing: $HAY"
[ -d "$DATA" ]   || die "$DATA missing"
[ $((SCALE*ORIG)) -eq $T ] || die "YaRN invariant: $SCALE * $ORIG != $T"
PSZ=$(stat -c%s "$PARENT")
[ "$PSZ" = "5225752094" ] || die "parent wrong size: $PSZ (want 5225752094)"
ntr=$(ls "$DATA"/*train*.bin 2>/dev/null | wc -l)
nva=$(ls "$DATA"/*val*.bin 2>/dev/null | wc -l)
[ "$ntr" -ge 1 ] && [ "$nva" -ge 1 ] || die "need train+val shards in $DATA (have $ntr/$nva)"
have=$(( $(du -cb "$DATA"/*train*.bin | tail -1 | cut -f1) / 2 ))
FREEG=$(df -BG --output=avail / | tail -1 | tr -dc 0-9)
[ "$FREEG" -ge 100 ] || die "only ${FREEG} GB free; 8-rank checkpoints need ~90 GB"

say "=== CONTINUE 64k b5 to a FULL EPOCH (8x H100, cp=$NP) ==="
say "parent: ext64k run3 step 695 (64k model), weights-only via CP_INIT_FROM"
say "YaRN SCALE=$SCALE ORIG=$ORIG -> $((SCALE*ORIG)) == CP_T=$T  OK"
say "b5: $ntr train shards = $(echo "scale=3; $have/1000000000" | bc) B tokens on disk"
say "disk: ${FREEG} GB free"

COMMON="CP_SIZE=$NP CP_ATTN_MODE=ring CP_ATTN_FUSION=split CP_T=$T \
YARN_SCALE=$SCALE YARN_ORIG_MAXPOS=$ORIG \
CP_ZERO=1 CP_ZERO_V2=1 CP_ZERO_SYMMETRIC=1 CP_BF16=1 CP_OPTIMIZER=muon \
CP_WARMUP=$WARM CP_REWARMUP=$WARM CP_REWARMUP_PEAK=0.3"
ARCH="CP_N_EMBD=1536 CP_N_LAYER=36 CP_N_HEAD=12 CP_N_KVHEAD=4 CP_FFN=4096 CP_WEIGHT_TYING=0"

# ---- PHASE 1: probe -- does 64k fit in 80 GB at cp=8? ----------------------
if [ ! -f "$LOG/probe.ok" ]; then
  say "PHASE 1: 3-step probe (8,192 tok/rank; extrapolates to ~67 GB of 81.5 GB)"
  env $ARCH $COMMON CP_STABLE=0 CP_CKPT=0 \
      CP_INIT_FROM="$PWD/$PARENT" CP_DATA_ROOT="$PWD/$DATA" \
      CP_B=1 CP_GLOBAL_BATCH=$T CP_MAX_STEPS=3 \
      CUDA_VISIBLE_DEVICES=$GPUS mpirun --allow-run-as-root -np $NP "$EXE" \
      > "$LOG/probe.log" 2>&1
  RC=$?
  # The probe must PROVE it trained, not merely fail to say "OOM". The first
  # version of this gate only grepped for OOM, so a probe that aborted on every
  # rank with `sdpa_split_rope_once_forward: build with -DCP_FUSED_ROPE` was
  # declared "PROBE OK" and the real run then died 73 s in. Three independent
  # checks now, and the peak-memory parse is one of them: an empty peak means
  # no step ever completed.
  if grep -qaiE 'out of memory|bad_alloc' "$LOG/probe.log"; then
    say "PROBE OOM -- 64k does NOT fit at cp=8 on 80 GB cards."
    say "  There is no further lever on 8 GPUs: CP_B is 1 and there is no recompute flag."
    grep -aoE 'cudaMallocAsync\(size=[0-9]+\) failed' "$LOG/probe.log" | head -2 | sed 's/^/    /' >&2
    exit 1
  fi
  if grep -qaE 'terminate called|std::runtime_error|Signal: Aborted' "$LOG/probe.log"; then
    say "PROBE CRASHED (not OOM). First error:"
    grep -aA1 'terminate called' "$LOG/probe.log" | grep -a 'what()' | head -1 | sed 's/^/    /' >&2
    exit 1
  fi
  NSTEP=$(grep -acE '^step +[0-9]+ \|' "$LOG/probe.log")
  PEAK=$(grep -aoE 'mem: [0-9]+MB' "$LOG/probe.log" | tail -1)
  [ "$RC" -eq 0 ]     || { say "PROBE exited $RC"; tail -15 "$LOG/probe.log" >&2; exit 1; }
  [ "$NSTEP" -ge 1 ]  || { say "PROBE produced NO step lines"; tail -15 "$LOG/probe.log" >&2; exit 1; }
  [ -n "$PEAK" ]      || { say "PROBE reported no peak memory"; tail -15 "$LOG/probe.log" >&2; exit 1; }
  grep -aE '^step +[0-9]+ \|' "$LOG/probe.log" | sed 's/^/  /' >&2
  say "PROBE OK: $NSTEP steps, peak $PEAK of 81559 MiB/card"
  touch "$LOG/probe.ok"
fi

# ---- step count: a full epoch over what is on disk -------------------------
# The probe ran grad_accum=1. Its dt is NOT the per-step cost -- multiplying it
# by grad_accum overstated the real rate 5.8x on the 64k run (74.0 vs 12.85
# s/step) because fixed per-step costs got multiplied too. Reported for
# information only; the step count comes from the DATA, not the clock.
SPS=$($PY - "$LOG/probe.log" $GB $T <<'PYEOF'
import re,sys,statistics
t=open(sys.argv[1],errors='ignore').read()
d=[float(x) for x in re.findall(r'dt: ([0-9.]+)ms', t)]
if not d: print("0.0"); raise SystemExit
probe_accum=1
real_accum=int(sys.argv[2])//int(sys.argv[3])
per=statistics.median(d[1:]) if len(d)>2 else d[-1]
print(f"{per/1000.0*real_accum/probe_accum:.3f}")
PYEOF
)
say "probe rate (UPPER BOUND, inflated by fixed costs x grad_accum): ${SPS} s/step"

STEPS=${STEPS_OVERRIDE:-$(( have / GB ))}
STABLE=$(( STEPS - WARM - TAIL ))
[ "$STABLE" -lt 1 ] && STABLE=1
# bluscriptCP.cpp:1006 dies if warmup + stable >= max_steps - 1
[ $(( WARM + STABLE )) -lt $(( STEPS - 1 )) ] || die "illegal WSD: $WARM+$STABLE >= $STEPS-1"
say "PHASE 2: $STEPS steps x $GB = $(echo "scale=3; $STEPS*$GB/1000000000" | bc) B tokens = a full b5 epoch"
say "  WSD: warmup $WARM, stable $STABLE, decay tail $TAIL -> final checkpoint IS decayed"

if [ -f "$LOG/cont.ckpt" ] && [ -f "$(cat "$LOG/cont.ckpt")" ]; then
  say "already trained -> $(cat "$LOG/cont.ckpt")"
else
  # Reclaim leaked .zero_g* sidecars while training runs: CP_CKPT_KEEP prunes
  # .ckpt but NOT the sidecars (that leak filled a 193 GB disk on 2026-09-09).
  ( while sleep 300; do
      for s in checkpoints_bluscriptcp/rank_*/*.zero_g*; do
        [ -e "$s" ] || continue
        base=${s%.zero_g*}
        [ -f "$base.ckpt" ] || rm -f "$s"
      done
    done ) & JANITOR=$!
  trap 'kill $JANITOR 2>/dev/null' EXIT

  env $ARCH $COMMON CP_STABLE=$STABLE \
      CP_CKPT=1 CP_CKPT_FREQ=$FREQ CP_CKPT_KEEP=$KEEP CP_CKPT_NEW_RUN=1 \
      CP_INIT_FROM="$PWD/$PARENT" CP_DATA_ROOT="$PWD/$DATA" \
      CP_B=1 CP_GLOBAL_BATCH=$GB CP_MAX_STEPS=$STEPS \
      CUDA_VISIBLE_DEVICES=$GPUS mpirun --allow-run-as-root -np $NP "$EXE" \
      > "$LOG/cont.log" 2>&1
  kill $JANITOR 2>/dev/null; trap - EXIT

  grep -qaiE 'out of memory|bad_alloc' "$LOG/cont.log" && die "OOM in the full run"
  RN=$(grep -aoE 'CP_Training_log[0-9]+' "$LOG/cont.log" | head -1 | grep -oE '[0-9]+')
  [ -n "$RN" ] || die "no run number"
  CKF=$(ls -t $CK/blumodelcp_run${RN}_step_*.ckpt 2>/dev/null | head -1)
  [ -n "$CKF" ] || die "run $RN wrote NO checkpoint"
  n=$(ls checkpoints_bluscriptcp/rank_*/"$(basename "$CKF")" 2>/dev/null | wc -l)
  [ "$n" -ge "$NP" ] || die "only $n/$NP rank shards for $(basename "$CKF")"
  echo "$CKF" > "$LOG/cont.ckpt"; echo "$RN" > "$LOG/runnum"
  say "cont64k -> run $RN, $(basename "$CKF"), $(grep -aE '^step ' "$LOG/cont.log" | tail -1 | grep -oE 'loss: [0-9.]+')"
  say "  peak mem $(grep -aoE 'mem: [0-9]+MB' "$LOG/cont.log" | tail -1)"
  say "  final lr $(grep -aE '^step ' "$LOG/cont.log" | tail -1 | grep -oE 'lr [0-9.e-]+')  (want ~6e-05 = decayed)"
fi

# ---- PHASE 3: eval, 4 lengths in parallel ----------------------------------
EXT=$(basename "$(cat "$LOG/cont.ckpt")")
say "PHASE 3: eval cont64k -- 4 lengths, one GPU each"
# 65512 + MAX_NEW 24 = 65536 exactly; 65528 would pad to 65792 and abort.
for LEN in 8192 16360 32744 65512; do
  ( GEN_DATA_ROOT=$DATA HAY="$HAY" PY=$PY OUT=cont64k_len_$LEN TASKS="niah_single" \
    CKPTS="cont64k" N=100 PROMPT_LEN=$LEN MAX_NEW=24 T=$T \
    DEPTHS="0.0,0.25,0.5,0.75,1.0" YARN_ORIG=$ORIG YARN=$SCALE \
    GPUS=$(( (LEN==8192)?0:(LEN==16360)?1:(LEN==32744)?2:3 )) JOBS_PER_GPU=1 \
    BF16=1 GREEDY=1 CKPT_EXTRA="cont64k=$EXT" ./run_longeval.sh \
    >> "$LOG/eval_$LEN.log" 2>&1 ) &
done
wait
for LEN in 8192 16360 32744 65512; do
  [ -s "cont64k_len_$LEN/summary.csv" ] && say "  len $LEN -> $(sed -n 2p cont64k_len_$LEN/summary.csv)" \
                                        || say "  len $LEN -> NO RESULT"
done

# ---- PHASE 4: upload, VERIFIED ---------------------------------------------
say "PHASE 4: upload"
{
  echo "# 64k b5 continuation to a full epoch -- $(date -u +%Y-%m-%d)"
  echo
  echo "Parent: ext64k run3 step 695 (67.0/65.0/52.0/29.0 at 8k/16k/32k/64k),"
  echo "        loaded WEIGHTS-ONLY: ZeRO sidecars cannot cross world_size 4 -> 8."
  echo "Data:   b5_65536_131072, $ntr shards = $(echo "scale=3; $have/1000000000" | bc) B tokens."
  echo "Train:  $STEPS steps x $GB = $(echo "scale=3; $STEPS*$GB/1000000000" | bc) B tokens = a full b5 epoch."
  echo "        The loader restarts at token 0, so b5's first 42% is seen a"
  echo "        second time and the rest for the first time."
  echo "        WSD warmup $WARM / stable $STABLE / decay tail $TAIL."
  echo "        8x H100, cp=$NP, B=1 x grad_accum $((GB/T))."
  echo "Eval:   niah_single, N=100, greedy, shared haystack b2_8192_16384 val."
  echo
  echo "## Results"; echo
  echo "| context | niah_single |"; echo "|---|---|"
  for LEN in 8192 16360 32744 65512; do
    v=$(sed -n 2p "cont64k_len_$LEN/summary.csv" 2>/dev/null | cut -d, -f2)
    printf "| %s | %s |\n" "$LEN" "${v:-n/a}"
  done
  echo
  echo "Parent for comparison: 67.0 / 65.0 / 52.0 / 29.0."
} > "$LOG/RESULTS.md"

# Upload, then CONFIRM the remote byte size. A missing source is a LOUD failure:
# the silent `[ -e "$1" ] || return 0` in ext64k_b5.sh is what lost the step-695
# checkpoint -- the CLI never errored and nothing was logged.
UPOK=1
up(){ # src dst
  local src=$1 dst=$2
  if [ ! -e "$src" ]; then say "  MISSING SOURCE (NOT UPLOADED): $src"; UPOK=0; return 1; fi
  if $HF upload "$REPO" "$src" "$dst" >>"$LOG/upload.log" 2>&1; then say "  sent $dst"
  else say "  UPLOAD FAILED $dst"; UPOK=0; return 1; fi
}
verify(){ # localfile remotepath
  $PY - "$REPO" "$1" "$2" <<'PYEOF'
import sys,os
from huggingface_hub import HfApi
repo,loc,rem=sys.argv[1],sys.argv[2],sys.argv[3]
want=os.path.getsize(loc)
got={f.rfilename:(f.size or 0) for f in HfApi().repo_info(repo,files_metadata=True).siblings}.get(rem)
print(f"  {'VERIFIED' if got==want else 'MISMATCH/ABSENT'} {rem} local={want:,} remote={got}")
sys.exit(0 if got==want else 1)
PYEOF
}

up "$LOG/RESULTS.md" "docs/CONT64K_B5_RESULTS_$(date -u +%Y-%m-%d).md"
for LEN in 8192 16360 32744 65512; do up "cont64k_len_$LEN" "logs/cont64k_len_$LEN/"; done
up "$LOG" logs/cont64k/
up "$(basename "$0")" "scripts/$(basename "$0")"

RN=$(cat "$LOG/runnum" 2>/dev/null)
STEP=$(basename "$(cat "$LOG/cont.ckpt")" | grep -oE 'step_[0-9]+' | cut -d_ -f2)
say "uploading the model itself (the part that was lost yesterday)"
SRC="$CK/blumodelcp_run${RN}_step_${STEP}.ckpt"
DST="checkpoints/cont64k_b5_rank0_step_${STEP}.ckpt"
if up "$SRC" "$DST"; then verify "$SRC" "$DST" | tee -a "$M" || UPOK=0; fi
for r in $(seq 0 $((NP-1))); do
  S="checkpoints_bluscriptcp/rank_$r/blumodelcp_run${RN}_step_${STEP}.zero_g1"
  D="checkpoints/cont64k_b5_rank${r}_step_${STEP}.zero_g1"
  if up "$S" "$D"; then verify "$S" "$D" | tee -a "$M" || UPOK=0; fi
done

[ "$UPOK" = 1 ] && say "=== CONT64K DONE -- all uploads verified, safe to tear down ===" \
                || say "=== CONT64K DONE but UPLOADS INCOMPLETE -- DO NOT TEAR DOWN ==="