fix(s31): close 1 review MAJOR — VECLIB_MAXIMUM_THREADS + honest determinism claim + the missing pin guard
This commit is contained in:
parent
b9dd91cdbe
commit
fb4c593924
2 changed files with 83 additions and 9 deletions
|
|
@ -17,9 +17,20 @@ Registered in ``tests/test_okf.py``'s ``_MAF_FREE_MODULES`` (direct-import AST g
|
|||
catches a lazy runtime import an import-time guard would miss — by
|
||||
``tests/test_semretrieval_loadbearing.py``.
|
||||
|
||||
Determinism: BLAS reduction order depends on the thread count, so the thread pins below are set
|
||||
before numpy is imported (this module is the only numpy importer in the package). Vectors are
|
||||
float64 and C-contiguous; ranking uses the total-order key ``(-round(score, 9), id)``.
|
||||
Determinism, stated precisely because the two halves rest on different things:
|
||||
|
||||
* **Ranking order** is guaranteed by the total-order key ``(-round(score, 9), id)`` — rounding
|
||||
absorbs last-bit float noise and ``id`` settles exact ties. This is the guarantee that actually
|
||||
holds, and it does NOT depend on the thread pins. It has to do real work: ``np.dot`` over two
|
||||
separately allocated but bit-identical vectors can differ by one ulp, because the reduction path
|
||||
varies with buffer alignment. Rounding is what makes that unobservable in the ranking.
|
||||
* **Byte-identical vector artifacts** (the ``.npy``/``.jsonl`` store, across machines) are what the
|
||||
thread pins below defend: BLAS reduction order depends on the thread count, and ``FakeEmbedder``
|
||||
calls ``np.linalg.norm``, which can dispatch to BLAS. The pins are defence-in-depth for that
|
||||
narrower claim — never the basis of the ranking guarantee.
|
||||
|
||||
Vectors are float64 and C-contiguous. This module is the only numpy importer in the package, which
|
||||
is what makes an import-time pin viable at all.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -31,10 +42,21 @@ from collections.abc import Callable, Sequence
|
|||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
||||
|
||||
# Pin BLAS threads BEFORE numpy is imported — OpenBLAS reads these at import time, and a
|
||||
# thread-count-dependent reduction order is the one thing that makes float64 dot products drift
|
||||
# between environments. Pinned to 1 => bit-exact run-to-run. ``setdefault`` so an operator can
|
||||
# still override deliberately.
|
||||
# Pin BLAS threads BEFORE numpy is imported — every one of these is latched by the time the numpy
|
||||
# import completes (measured: setting them afterwards has no effect, even before the first BLAS
|
||||
# call), so placement above the import is load-bearing, not stylistic. ``setdefault`` so an
|
||||
# operator can still override deliberately.
|
||||
#
|
||||
# One variable per backend, because which one BITES depends on what numpy links:
|
||||
# VECLIB_MAXIMUM_THREADS -> Accelerate (macOS; what numpy >= 2.0 links here by default)
|
||||
# OPENBLAS_NUM_THREADS -> OpenBLAS (numpy's default wheel on Linux/Windows, and numpy 1.x)
|
||||
# MKL_NUM_THREADS -> Intel MKL (conda-forge / Intel distributions)
|
||||
# OMP_NUM_THREADS -> the OpenMP runtime underneath several of the above
|
||||
# On THIS build the effective one is VECLIB_MAXIMUM_THREADS; the other three are measured no-ops
|
||||
# against Accelerate. They stay because they are the correct knobs for an OpenBLAS/MKL/OpenMP
|
||||
# deployment — that is a PORTABILITY argument, not a determinism one, and the distinction matters:
|
||||
# before VECLIB was added, this block pinned nothing at all on the machine it ran on.
|
||||
os.environ.setdefault("VECLIB_MAXIMUM_THREADS", "1")
|
||||
os.environ.setdefault("OPENBLAS_NUM_THREADS", "1")
|
||||
os.environ.setdefault("OMP_NUM_THREADS", "1")
|
||||
os.environ.setdefault("MKL_NUM_THREADS", "1")
|
||||
|
|
@ -48,8 +70,11 @@ if TYPE_CHECKING: # verdicts imports agent_framework — keep it out of the run
|
|||
# The structural score, passed in rather than imported — see module docstring.
|
||||
SimilarityFn = Callable[[ProposalFeatures, ProposalFeatures], float]
|
||||
|
||||
# Dimension of the fake embedding. Small enough that a 500+ verdict base is a trivial matmul,
|
||||
# large enough that distinct features do not collide.
|
||||
# Dimension of the fake embedding. Large enough that distinct features do not collide, small
|
||||
# enough that a 500+ verdict base stays cheap. Note there is no matmul on the ranking path:
|
||||
# ``HybridRanker.rank`` calls ``cosine()`` once per candidate from inside the sort key — a Python
|
||||
# loop over per-pair ``np.dot`` on 64-vectors — and re-invokes the embedder for every candidate on
|
||||
# every call, caching nothing. Fine at this scale; the thing to change first if it ever is not.
|
||||
EMBED_DIM = 64
|
||||
|
||||
# Blend weight for HybridRanker: score = weight * cosine + (1 - weight) * structural.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue