Chronaxis用户手册English
本页目录

chronaxis.execution.memory_reader

源码基线:8904c2f744951857659abd40aaa3535d57faf53c

1"""在既有内存适配器之外预留输入、批次与协议状态预算。23输入SourceSpec本来已完整存在于调用方内存;封装构造前核算它和信号声明,4不声称控制过去的分配或全进程RSS。输出为既有适配器实际SignalBatch,5归一化次数、backend、分批、原顺序、watermark/EOF和异常原因不改变。6每请求预留最坏两个交接批次及来源pending状态,再创建/推进内部生成器;7NumPy字符串按最大原宽度×N核算Unicode数组/临时副本,不能只计原字符串总长。8超限明确失败,不偷偷减小batch_rows、切换后端或丢样本。所有预算至EOF/close释放,9外部长期保存owned批次不在本封装内部预算保证内;不读取CSV、旧日志或DBC。10"""1112# 原适配器和结构模型负责输入契约,资源账本仅控制新执行层开销。13from collections.abc import Iterator14from ..core.binding import SignalDefinition15from ..core.models import SourceSpec16from ..results.budget import ResourceBudget17from ..results.spool import _cleanup_owner, _reserve_owned_ram, _release_owned_ram18from ..sources.memory import MemorySourceAdapter19from ..sources.models import ReadRequest, SignalBatch, SourceDescription20from ..sources.protocol import SourceClosedError21from .history import _metadata_bytes222324class BudgetedMemorySourceAdapter:25    """共享一个输入预留,为各读取请求保留独立批次/协议最坏预算。2627adapter只在预留成功后建立;info存每别名实际投影总数与最大原字符串字符数。28active登记活动请求,close尝试关闭全部并释放输入引用,原SourceSpec不修改。29_uninstalled_read保构造或登记未返回的真实请求;_closing先拒绝新读,closed仅在30所有子请求、原适配器及本层_input_cost实际释放后成立。31"""3233    def __init__(self, budget: ResourceBudget, source: SourceSpec, session_id: str,34                 signals: tuple[SignalDefinition, ...], *, backend: str = 'numpy', clock_only=(), retain_original=True):35        """核算已有完整输入后建立原适配器,不提前生成数组。3637        参数: budget为共享账本;source/session_id/signals/backend与既有内存入口相同。38            clock_only只取时间;retain_original=False不生成原值列,默认仍保留旧行为。39        返回: 新封装。异常: 配置/预算/原适配器初始化原错;持续清理失败附40            memory_source_cleanup_owner,恢复后从该真实owner重试close。41        """42        if not isinstance(budget, ResourceBudget) or not isinstance(source, SourceSpec) or type(signals) is not tuple or any(not isinstance(item, SignalDefinition) for item in signals):43            raise TypeError('预算内存来源需要账本/SourceSpec/typed信号tuple')44        # 所有保留输入和固定描述保守计数,未知对象不参与任意序列化。45        cost = 16384 + _metadata_bytes((source, session_id, signals)) * 1646        self.budget = self._budget = budget47        self.closed = self._closing = False48        self._input_cost = self._held_cost = 049        self._adapter = self._uninstalled_read = None50        self.active: set[_BudgetedBatches] = set()51        self.info = {}52        try:53            # 直接申请返回途中取消也由同一槽登记;不靠子调用后的共享余额补账。54            _reserve_owned_ram(self, cost, '_input_cost')55            self._adapter = MemorySourceAdapter(source, session_id, signals, backend=backend,56                clock_only=clock_only,retain_original=retain_original)57            self.info = {item.name: [0, 0] for item in signals}58            # 只遍历已经提供的memory原行,不重新读取旧输入或进行归一化。59            for row in source.rows if signals else ():60                for item in signals:61                    if item.field in row.values:62                        self.info[item.name][0] += 163                        value = None if item.name in clock_only else row.values[item.field]64                        if type(value) is str:65                            self.info[item.name][1] = max(self.info[item.name][1], len(value))66        except BaseException as exc:67            _cleanup_owner(self, exc, 'memory_source_cleanup_owner', '预算内存来源构造')68            raise6970    def _ensure_open(self) -> None:71        """拒绝已关闭来源,不以空批掩盖错误。7273        参数: 无。返回: None。异常: SourceClosedError为已关闭。74        """75        if self.closed or self._closing:76            raise SourceClosedError('预算内存来源已关闭或正在清理')7778    def describe(self) -> SourceDescription:79        """直接返回原适配器静态描述,不提升来源能力或观测证据状态。8081        参数: 无。返回: SourceDescription。异常: 关闭/原描述错误传播。82        """83        self._ensure_open()84        return self._adapter.describe()8586    def _request_cost(self, request: ReadRequest) -> int:87        """计算一个读取请求在全部next期间持有的最坏预留。8889        参数: request为当前会话/投影/批次上限。返回: 保守bytes,非RSS。90        异常: ValueError为请求会话/别名错误,其他预算错误由申请点报告。91        """92        self._ensure_open()93        if not isinstance(request, ReadRequest) or request.session_id != self._adapter.session_id or any(name not in self.info for name in request.signal_names):94            raise ValueError('预算内存来源请求会话/投影错误')95        total = sum(self.info[name][0] for name in request.signal_names)96        size = min(total, request.batch_rows)97        max_chars = max((self.info[name][1] for name in request.signal_names), default=0)98        # nbytes至多time/sequence各8、values8、valid1、quality2;4096覆盖复制、装箱/分组。99        per_batch = 65536 + len(request.signal_names) * 8192 + size * 4096100        if self._adapter.backend == 'numpy':101            # 原str可能形成(N,) UTF32定宽列,八倍覆盖原列/转换及模型验证临时副本。102            per_batch += size * max_chars * 4 * 8103        # yield时原生成器仍保留当前批,next构造下一批存在交接重叠,不只申请一套数组。104        # 协议pending最坏保留全部原投影身份并复制set;memory总样本数是已知有限事实。105        return per_batch * 2 + total * 512 + len(request.signal_names) * 2048106107    def iter_batches(self, request: ReadRequest) -> Iterator[SignalBatch]:108        """预留整次请求的最坏缓冲/状态,再创建实际批次生成器。109110        参数: request为既有读取请求,不改变batch_rows。返回: 独立预算游标。111        异常: 请求/预算/原来源错误传播;超限在数组归一化之前失败,持续清理112            失败附memory_read_cleanup_owner,来源也保留未返回read槽。113        """114        cost = self._request_cost(request)115        if self._uninstalled_read is not None:116            raise SourceClosedError('预算内存来源尚有未返回请求待清理')117        try:118            iterator = _BudgetedBatches(self, request, cost)119        except BaseException as exc:120            # 构造未返回的真实read先归来源,不能只保费用而遗失原生成器。121            self._uninstalled_read = getattr(exc, 'memory_read_cleanup_owner', None)122            raise123        self._uninstalled_read = iterator124        try:125            self.active.add(iterator)126        except BaseException as exc:127            # add可能在登记前或后抛错;只关当前read,原注册错误不被close取消替代。128            _cleanup_owner(iterator, exc, 'memory_read_cleanup_owner', '预算内存请求登记')129            raise130        self._uninstalled_read = None131        return iterator132133    def close(self) -> None:134        """关闭所有请求及原适配器,释放本封装预留并保留调用方原输入。135136        参数: 无。返回: None,幂等。异常: 首个原关闭错误附memory_source_cleanup_owner;137            其他独立read也尝试关闭,子责任未完成时保留来源合同与全部未释放槽。138        """139        if self.closed:140            return141        self._closing = True142        try:143            first_error = None144            # 未登记read仍属于本来源;显式槽与活动set可指向同对象,close幂等。145            if self._uninstalled_read is not None:146                try:147                    self._uninstalled_read.close()148                except BaseException as exc:149                    first_error = exc150            for iterator in tuple(self.active):151                try:152                    iterator.close()153                except BaseException as exc:154                    if first_error is None:155                        first_error = exc156                    else:157                        first_error.add_note(f'其他内存请求关闭失败: {type(exc).__name__}')158            if first_error is not None:159                raise first_error160            if self._adapter is not None:161                self._adapter.close()162                self._adapter = None163            # 子生成器与原来源全关后才丢输入引用;固定费逐笔确认释放前后取消。164            self.info.clear()165            _release_owned_ram(self, self._input_cost, '_input_cost')166            if self._held_cost == 0:167                self.closed = True168        except BaseException as exc:169            exc.memory_source_cleanup_owner = self170            raise171172173class _BudgetedBatches:174    """持有一请求的预留/原生成器/当前批,EOF或close时抵销一次预留。"""175176    def __init__(self, owner: BudgetedMemorySourceAdapter, request: ReadRequest, cost: int):177        """先申请再创建原请求,构造失败不保留请求状态。178179        参数: owner为预算来源;request为原请求;cost为最坏预留。返回: 独立游标。180        异常: 预算或原请求初始化原错,持续清理失败附memory_read_cleanup_owner;181            未实际入账不抵销,返回途中取消仍登记原cost槽。182        """183        self.owner, self.closed, self.current = owner, False, None184        self._budget, self._held_cost, self.cost = owner.budget, 0, 0185        self.iterator = None186        self._closing = False187        try:188            # 先装恢复槽再申请;旧最坏整次请求费用公式保持原数值。189            _reserve_owned_ram(self, cost, 'cost')190            self.iterator = owner._adapter.iter_batches(request)191        except BaseException as exc:192            _cleanup_owner(self, exc, 'memory_read_cleanup_owner', '预算内存请求构造')193            raise194195    def __iter__(self) -> '_BudgetedBatches':196        """返回本独立游标。197198        参数: 无。返回: self。异常: 无。199        """200        return self201202    def __next__(self) -> SignalBatch:203        """在整次预留覆盖下推进原适配器,不重复归一化或改进度。204205        参数: 无。返回: 原适配器实际批次。异常: EOF/关闭/原来源错误,故障清理保留根因。206        """207        self.owner._ensure_open()208        if self.closed:209            raise StopIteration210        if self._closing:211            raise SourceClosedError('预算内存请求正在清理')212        try:213            self.current = next(self.iterator)214            return self.current215        except StopIteration:216            # 真实耗尽后关闭失败必须传播该失败,不能用StopIteration伪报正常完成。217            self.close()218            raise219        except BaseException as exc:220            _cleanup_owner(self, exc, 'memory_read_cleanup_owner', '预算内存请求推进')221            raise222223    def close(self) -> None:224        """关闭本请求原生成器,释放当前批引用和最坏预留,其他请求保持独立。225226        参数: 无。返回: None,幂等。异常: 原生成器或释放错误附memory_read_cleanup_owner;227            原生成器未成功关闭时保留原对象和费用,其他请求保持独立。228        """229        if self.closed:230            return231        self._closing = True232        try:233            if self.iterator is not None:234                self.iterator.close()235                # 未开始生成器close不执行finally,只解除本read的原登记,不动平行read。236                if self.owner._adapter is not None:237                    self.owner._adapter.active.discard(self.iterator)238            self.current = self.iterator = None239            _release_owned_ram(self, self.cost, 'cost')240            # 费用真正抵销后才从父解除,释放后取消可重试而不会重复扣账。241            self.owner.active.discard(self)242            if self.owner._uninstalled_read is self:243                self.owner._uninstalled_read = None244            self.closed = True245        except BaseException as exc:246            exc.memory_read_cleanup_owner = self247            raise248
Chronaxis 用户手册 · 离线阅读