tan-cli 0.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tan/__init__.py +0 -0
- tan/__main__.py +120 -0
- tan/cli.py +939 -0
- tan/command_table.py +265 -0
- tan/commands/__init__.py +8 -0
- tan/commands/bootstrap_cmd.py +4856 -0
- tan/commands/bootstrap_patches.py +164 -0
- tan/commands/bootstrap_toolchain_fetch.py +155 -0
- tan/commands/bootstrap_toolchain_pin.py +36 -0
- tan/commands/build/__init__.py +1 -0
- tan/commands/build/configure_inputs.py +277 -0
- tan/commands/build/execute.py +1852 -0
- tan/commands/build/host_python.py +59 -0
- tan/commands/build/link_stale.py +78 -0
- tan/commands/build/manifest.py +428 -0
- tan/commands/build/materialise.py +81 -0
- tan/commands/build/token_substitution.py +381 -0
- tan/commands/build/toolchain.py +482 -0
- tan/commands/build/user_defines_wipe.py +115 -0
- tan/commands/build/workspace_patches.py +65 -0
- tan/commands/build_cmd.py +2236 -0
- tan/commands/build_output.py +331 -0
- tan/commands/clean_cmd.py +1183 -0
- tan/commands/completion_cmd.py +786 -0
- tan/commands/debug_config_cmd.py +2425 -0
- tan/commands/diff_cmd.py +940 -0
- tan/commands/doctor_cmd.py +4767 -0
- tan/commands/examples_cmd.py +644 -0
- tan/commands/explain_cmd.py +1319 -0
- tan/commands/faultdecode_cmd.py +931 -0
- tan/commands/flash_atoc_guard.py +240 -0
- tan/commands/flash_cmd.py +5731 -0
- tan/commands/flash_mram_guard.py +150 -0
- tan/commands/flash_ram.py +578 -0
- tan/commands/flash_raw.py +583 -0
- tan/commands/generate_cmd.py +1456 -0
- tan/commands/image_cmd.py +753 -0
- tan/commands/init_cmd.py +1740 -0
- tan/commands/inspect_cmd.py +377 -0
- tan/commands/kconfig_cmd.py +734 -0
- tan/commands/model_cmd.py +1514 -0
- tan/commands/model_device_cmd.py +351 -0
- tan/commands/model_device_live.py +234 -0
- tan/commands/model_host_cmd.py +430 -0
- tan/commands/model_zoo_cmd.py +363 -0
- tan/commands/monitor_actions.py +125 -0
- tan/commands/monitor_cmd.py +626 -0
- tan/commands/monitor_session.py +505 -0
- tan/commands/new_som_cmd.py +1522 -0
- tan/commands/pinmux_cmd.py +568 -0
- tan/commands/presets_cmd.py +1123 -0
- tan/commands/probe_cmd.py +421 -0
- tan/commands/reset_cmd.py +354 -0
- tan/commands/reset_confirm.py +168 -0
- tan/commands/run_cmd.py +637 -0
- tan/commands/scaffold_cmd.py +490 -0
- tan/commands/sdk_cmd.py +1723 -0
- tan/commands/size_cmd.py +1020 -0
- tan/commands/support_bundle_cmd.py +1073 -0
- tan/commands/trace_cmd.py +341 -0
- tan/commands/validate_cmd.py +2009 -0
- tan/commands/west_forward_cmd.py +736 -0
- tan/commands/workspace_patch_check.py +296 -0
- tan/core/__init__.py +1 -0
- tan/core/alp_conf_pregen.py +100 -0
- tan/core/artifact_provenance.py +240 -0
- tan/core/atoc_guard.py +276 -0
- tan/core/atoc_guard_messages.py +232 -0
- tan/core/atoc_replacement.py +118 -0
- tan/core/atomic_write.py +178 -0
- tan/core/board_context.py +351 -0
- tan/core/board_diagnostic.py +111 -0
- tan/core/board_files.py +221 -0
- tan/core/board_validator.py +542 -0
- tan/core/board_validator_compat.py +196 -0
- tan/core/board_validator_run.py +129 -0
- tan/core/board_validator_schema.py +186 -0
- tan/core/board_validator_skew.py +46 -0
- tan/core/board_yaml_edit.py +295 -0
- tan/core/board_yaml_pos.py +118 -0
- tan/core/bootstrap.py +2572 -0
- tan/core/build_plan.py +625 -0
- tan/core/consent.py +104 -0
- tan/core/console_filter.py +245 -0
- tan/core/debug_launch.py +1803 -0
- tan/core/devicetree_lint.py +168 -0
- tan/core/dir_removal.py +229 -0
- tan/core/doctor_git.py +172 -0
- tan/core/doctor_libraries.py +253 -0
- tan/core/doctor_render.py +185 -0
- tan/core/doctor_scope.py +66 -0
- tan/core/doctor_setools.py +237 -0
- tan/core/doctor_stale.py +180 -0
- tan/core/document_guards.py +723 -0
- tan/core/dp_id.py +97 -0
- tan/core/elf_load.py +63 -0
- tan/core/error_catalog.py +171 -0
- tan/core/example_catalog.py +499 -0
- tan/core/example_facets.py +151 -0
- tan/core/faultdecode.py +521 -0
- tan/core/flash_plan.py +2501 -0
- tan/core/flow_d_report.py +381 -0
- tan/core/flow_d_uptime.py +164 -0
- tan/core/fs_confine.py +130 -0
- tan/core/global_flags.py +230 -0
- tan/core/host_python.py +210 -0
- tan/core/image_bundle.py +247 -0
- tan/core/inert.py +147 -0
- tan/core/jlink_binary.py +128 -0
- tan/core/jlink_probe.py +481 -0
- tan/core/jsonc_splice.py +404 -0
- tan/core/launch_provenance.py +257 -0
- tan/core/link_refusal.py +76 -0
- tan/core/metadata_schema.py +376 -0
- tan/core/model_check.py +227 -0
- tan/core/model_device.py +449 -0
- tan/core/model_doctor.py +198 -0
- tan/core/model_host.py +48 -0
- tan/core/model_list.py +112 -0
- tan/core/model_zoo.py +353 -0
- tan/core/module_template.py +340 -0
- tan/core/mram_link.py +87 -0
- tan/core/new_som_ospi.py +94 -0
- tan/core/os_class.py +159 -0
- tan/core/patch_paths.py +87 -0
- tan/core/pending.py +40 -0
- tan/core/plain_zephyr_plan.py +138 -0
- tan/core/plan_exec.py +373 -0
- tan/core/plan_tokens.py +600 -0
- tan/core/probe.py +155 -0
- tan/core/probe_plan.py +237 -0
- tan/core/proxy.py +168 -0
- tan/core/publish.py +31 -0
- tan/core/python_floor.py +178 -0
- tan/core/ram_run.py +694 -0
- tan/core/ram_watch.py +231 -0
- tan/core/raw_write.py +160 -0
- tan/core/reset_plan.py +56 -0
- tan/core/run.py +161 -0
- tan/core/scaffold.py +2203 -0
- tan/core/scaffold_selftest_identity.py +225 -0
- tan/core/sdk_default_registry.py +565 -0
- tan/core/sdk_discovery.py +996 -0
- tan/core/sdk_removal.py +465 -0
- tan/core/serial_actions.py +175 -0
- tan/core/serial_capture.py +185 -0
- tan/core/serial_url.py +45 -0
- tan/core/setools.py +608 -0
- tan/core/setools_scratch.py +243 -0
- tan/core/shapes.py +194 -0
- tan/core/size.py +610 -0
- tan/core/som_buildability.py +204 -0
- tan/core/som_schema_version.py +62 -0
- tan/core/subprocess_env.py +201 -0
- tan/core/sysbuild_image.py +94 -0
- tan/core/system_manifest.py +827 -0
- tan/core/text_layout.py +73 -0
- tan/core/timestamp.py +182 -0
- tan/core/tool_lookup.py +383 -0
- tan/core/toolchain_pin.py +117 -0
- tan/core/toolchain_provision.py +1224 -0
- tan/core/trusted_path.py +134 -0
- tan/core/uboot_breakin.py +154 -0
- tan/core/uri_reference.py +415 -0
- tan/core/user_defines.py +152 -0
- tan/core/venv.py +332 -0
- tan/core/west_patches.py +143 -0
- tan/core/west_workspace_refusal.py +159 -0
- tan/core/zephyr_env.py +124 -0
- tan/env.py +185 -0
- tan/envelope.py +647 -0
- tan/exit_codes.py +12 -0
- tan/lazy_typer.py +185 -0
- tan/model/__init__.py +2 -0
- tan/model/_gen_fixture.py +132 -0
- tan/model/adapters/__init__.py +116 -0
- tan/model/adapters/cpu.py +26 -0
- tan/model/adapters/deepx.py +94 -0
- tan/model/adapters/drpai.py +242 -0
- tan/model/adapters/ethos_u.py +951 -0
- tan/model/adapters/executorch.py +55 -0
- tan/model/analyze.py +694 -0
- tan/model/build.py +304 -0
- tan/model/check.py +682 -0
- tan/model/manifest.py +625 -0
- tan/model/measure.py +216 -0
- tan/model/npu_ops_validate.py +130 -0
- tan/model/package.py +125 -0
- tan/model/perf.py +635 -0
- tan/model/perf_apply.py +721 -0
- tan/model/prep.py +193 -0
- tan/model/sram_fit.py +502 -0
- tan/model/targets.py +374 -0
- tan/model/tensorio.py +287 -0
- tan/net.py +73 -0
- tan/output_format.py +213 -0
- tan/planner/__init__.py +219 -0
- tan/planner/aperture.py +202 -0
- tan/planner/buildplan.py +1092 -0
- tan/planner/camera_owner.py +192 -0
- tan/planner/cameras.py +132 -0
- tan/planner/carveout.py +755 -0
- tan/planner/cli.py +274 -0
- tan/planner/headers.py +315 -0
- tan/planner/kconfig.py +2435 -0
- tan/planner/kconfig_symbols.py +473 -0
- tan/planner/libraries.py +677 -0
- tan/planner/link_target.py +237 -0
- tan/planner/linux_ownership.py +216 -0
- tan/planner/loader.py +1525 -0
- tan/planner/manifest.py +116 -0
- tan/planner/memory.py +161 -0
- tan/planner/memregion.py +56 -0
- tan/planner/models.py +533 -0
- tan/planner/orchestrator.py +874 -0
- tan/planner/ownership.py +203 -0
- tan/planner/partition.py +1160 -0
- tan/planner/paths.py +32 -0
- tan/planner/plain_slice.py +139 -0
- tan/planner/project_emit/__init__.py +60 -0
- tan/planner/project_emit/bom_netlist.py +375 -0
- tan/planner/project_emit/dts.py +580 -0
- tan/planner/project_emit/hw_info.py +221 -0
- tan/planner/project_emit/native_sim.py +136 -0
- tan/planner/project_emit/west_libs.py +213 -0
- tan/planner/project_loader.py +264 -0
- tan/planner/sdk_capability.py +203 -0
- tan/planner/sdk_compat.py +395 -0
- tan/planner/secure.py +350 -0
- tan/planner/slugs.py +394 -0
- tan/planner/som_metadata.py +325 -0
- tan/planner/strict_loaders.py +131 -0
- tan/planner/template.py +1037 -0
- tan/planner/template_pins.py +535 -0
- tan/planner/template_rewrite.py +714 -0
- tan/planner/topology.py +208 -0
- tan/planner/validate.py +365 -0
- tan/planner/whole_device_alias.py +67 -0
- tan/planner/zephyr_board.py +3510 -0
- tan/planner_cli.py +97 -0
- tan/planner_emit.py +617 -0
- tan/planner_root.py +119 -0
- tan/soc_ref.py +58 -0
- tan/templates/__init__.py +34 -0
- tan/templates/vendored/MANIFEST.md +1862 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-AEN801/testcase.yaml +53 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/board.yaml +38 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.conf +11 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/boards/native_sim_native_64.overlay +29 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/src/main.c +266 -0
- tan/templates/vendored/diagnostics/E1M-V2N101/testcase.yaml +53 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/board.yaml +38 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-AEN801/testcase.yaml +62 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/CMakeLists.txt +10 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/README.md +75 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/board.yaml +37 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.c +371 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/cold_chain.h +178 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/src/main.c +373 -0
- tan/templates/vendored/edge-ai/E1M-V2N101/testcase.yaml +62 -0
- tan/templates/vendored/iot/E1M-AEN801/CMakeLists.txt +14 -0
- tan/templates/vendored/iot/E1M-AEN801/README.md +103 -0
- tan/templates/vendored/iot/E1M-AEN801/board.yaml +69 -0
- tan/templates/vendored/iot/E1M-AEN801/prj.conf +15 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.c +196 -0
- tan/templates/vendored/iot/E1M-AEN801/src/cc3501e_bridge.h +173 -0
- tan/templates/vendored/iot/E1M-AEN801/src/main.c +262 -0
- tan/templates/vendored/iot/E1M-AEN801/testcase.yaml +68 -0
- tan/templates/vendored/minimal/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/README.md +108 -0
- tan/templates/vendored/minimal/E1M-AEN801/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-AEN801/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-AEN801/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-AEN801/testcase.yaml +49 -0
- tan/templates/vendored/minimal/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/README.md +108 -0
- tan/templates/vendored/minimal/E1M-V2N101/board.yaml +32 -0
- tan/templates/vendored/minimal/E1M-V2N101/prj.conf +8 -0
- tan/templates/vendored/minimal/E1M-V2N101/src/main.c +111 -0
- tan/templates/vendored/minimal/E1M-V2N101/testcase.yaml +49 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/README.md +120 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/boards/native_sim_native_64.overlay +40 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/CMakeLists.txt +13 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/main.c +171 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/prj.conf +17 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/peer/testcase.yaml +23 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/prj.conf +22 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/src/main.c +185 -0
- tan/templates/vendored/multicore-mailbox/E1M-AEN801/testcase.yaml +26 -0
- tan/templates/vendored/sensor/E1M-AEN801/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-AEN801/README.md +112 -0
- tan/templates/vendored/sensor/E1M-AEN801/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-AEN801/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-AEN801/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-AEN801/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-AEN801/testcase.yaml +52 -0
- tan/templates/vendored/sensor/E1M-V2N101/CMakeLists.txt +8 -0
- tan/templates/vendored/sensor/E1M-V2N101/README.md +112 -0
- tan/templates/vendored/sensor/E1M-V2N101/board.yaml +57 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.conf +10 -0
- tan/templates/vendored/sensor/E1M-V2N101/boards/native_sim_native_64.overlay +28 -0
- tan/templates/vendored/sensor/E1M-V2N101/prj.conf +5 -0
- tan/templates/vendored/sensor/E1M-V2N101/src/main.c +186 -0
- tan/templates/vendored/sensor/E1M-V2N101/testcase.yaml +52 -0
- tan/version.py +102 -0
- tan_cli-0.7.0.dist-info/METADATA +125 -0
- tan_cli-0.7.0.dist-info/RECORD +330 -0
- tan_cli-0.7.0.dist-info/WHEEL +5 -0
- tan_cli-0.7.0.dist-info/entry_points.txt +2 -0
- tan_cli-0.7.0.dist-info/top_level.txt +1 -0
tan/model/perf_apply.py
ADDED
|
@@ -0,0 +1,721 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""Tier 2's MATCHING half: turn a customer's identity into the ONE
|
|
3
|
+
`metadata/model_perf/` point that describes their machine, and turn that point
|
|
4
|
+
into a `BackendReport`. `tan.model.perf` is deliberately only the READER (it
|
|
5
|
+
finds points and refuses to rank them, by its own module docstring); this is
|
|
6
|
+
the DECIDER, and `tan.model.check.check_model_backends` is its only caller.
|
|
7
|
+
|
|
8
|
+
Split out of `tan.model.check` (tan-cli#791 review NIT (c)): this is pure
|
|
9
|
+
string/decision logic with no subprocess in it -- `check.py` was 789 lines
|
|
10
|
+
against the house `module_cap: 800`, and `MODULE_SIZE_BUDGET_LOG.md`'s own
|
|
11
|
+
`ethos_u.py` entry already named exactly this shape of growth for a
|
|
12
|
+
neighbouring module ("the extraction to make when this file next grows").
|
|
13
|
+
This module takes the already-resolved Ethos-U target as a PARAMETER
|
|
14
|
+
(`target`) rather than resolving it itself (`_headline_ethos_u_target` stays
|
|
15
|
+
in `check.py`, which also needs it for `--exact`) -- passing it down avoids a
|
|
16
|
+
`check.py` <-> `perf_apply.py` import cycle, and it costs nothing: the caller
|
|
17
|
+
already has it in hand before either half of `check_model_backends`' per-model
|
|
18
|
+
work runs.
|
|
19
|
+
|
|
20
|
+
Everything below answers ONE question -- "did Alp Lab already measure THIS
|
|
21
|
+
model, on THIS module, at THIS accelerator target?" -- and does nothing at all
|
|
22
|
+
when the answer is no. That is not a courtesy, it is the contract: the same
|
|
23
|
+
absence rule `npu-ops-v1` states for a missing op table applies here, so a
|
|
24
|
+
customer whose model has never been benched (which is most of them) must get
|
|
25
|
+
exactly the report they get today, note for note. `apply_perf_point`
|
|
26
|
+
therefore returns @report ITSELF on every non-match path rather than a
|
|
27
|
+
rebuilt copy, with no exception: "unchanged" is structural rather than a
|
|
28
|
+
promise.
|
|
29
|
+
|
|
30
|
+
A point's `target.core` is one of the fields that non-match rule protects
|
|
31
|
+
(tan-cli#791 round-2 review item 1 / round-3's evidence-driven narrowing). The
|
|
32
|
+
die a SoC spec describes may pair a given NPU to a specific core, or may not --
|
|
33
|
+
alp-sdk's own schema says a shared SoC-level NPU legitimately declares no
|
|
34
|
+
`paired_core` at all -- and `_resolve_perf_point` enforces exactly that
|
|
35
|
+
DECLARED fact about the accelerator actually being screened, never a fact
|
|
36
|
+
borrowed from a sibling NPU of the same backend that happens to declare one.
|
|
37
|
+
See `_resolve_perf_point`'s own docstring for the sourced silicon evidence
|
|
38
|
+
(the E8's Ethos-U85) that makes the distinction concrete.
|
|
39
|
+
|
|
40
|
+
A BENCH POINT CANNOT CLAIM A COVERAGE VERDICT (tan-cli#1115). Reconciling
|
|
41
|
+
`tan.model.perf.PerfPoint` against alp-sdk's REAL `model-perf-v1` schema
|
|
42
|
+
removed `npu_ops`/`cpu_ops` -- fields the schema never actually carries (see
|
|
43
|
+
that module's docstring, "DEAD FIELDS") -- so this module can no longer ask
|
|
44
|
+
`coverage_from_placement` for a bench-derived `npu_coverage`. A matched point
|
|
45
|
+
overlays only the figures alp-sdk's schema actually publishes today:
|
|
46
|
+
`arena_bytes`, `req_sram_kib`, and latency once a capture campaign times one
|
|
47
|
+
(merged per field, most-specific-wins, against whatever @report already had --
|
|
48
|
+
`_merge_figure`, PR review blocker 3: a point that measured only latency must
|
|
49
|
+
not blank a real `--exact` compile's own `arena_bytes`/`req_sram_kib`). This
|
|
50
|
+
is a real, deliberate narrowing of what tier 2 claims versus an earlier,
|
|
51
|
+
unreconciled version of this module (which asked a fictional
|
|
52
|
+
`measured.npu_ops`/`cpu_ops` pair for a "fits"/"partial"/"cpu-only" verdict no
|
|
53
|
+
alp-sdk document has ever actually carried): the schema's own description of
|
|
54
|
+
`perf.latency_ms` says a capture campaign "can record fit/SRAM alone until a
|
|
55
|
+
timing harness exists", and there is likewise no wire representation of an
|
|
56
|
+
op-by-op NPU/CPU split for `apply_perf_point` to have been reading in the
|
|
57
|
+
first place.
|
|
58
|
+
|
|
59
|
+
COVERAGE ITSELF IS WITHHELD, NOT INHERITED, UNLESS @report ALREADY CARRIES A
|
|
60
|
+
REAL PLACEMENT (PR review blocker 1 -- an earlier version of this PR
|
|
61
|
+
inherited `npu_coverage`/`ops`/`uncosted_cpu_op_count`/`npu_placement_
|
|
62
|
+
pct_real` from @report UNCONDITIONALLY, which silently promoted the static
|
|
63
|
+
screen's name-level, capped-positive ESTIMATE -- `docs/model-check-static-
|
|
64
|
+
screen.md`'s own vocabulary calls `partial`/`full-eligible` at
|
|
65
|
+
`basis: "static-screen"` explicitly "Not a placement claim" -- into a
|
|
66
|
+
`basis: "bench", confidence: "certain"` report, exactly the estimate-wearing-
|
|
67
|
+
a-measurement's-clothes failure this module's `compute_on_npu_pct_max`
|
|
68
|
+
handling already refuses two paragraphs down in `_perf_point_report`). The
|
|
69
|
+
rule: those four fields carry forward from @report ONLY when `report.basis ==
|
|
70
|
+
"compiled"` (a real, `--exact`-measured placement, safe to keep under
|
|
71
|
+
`confidence: "certain"`); otherwise the rebased report states
|
|
72
|
+
`npu_coverage: "undetermined"`, `ops: []`, `uncosted_cpu_op_count: 0`,
|
|
73
|
+
`npu_placement_pct_real: None` -- the SRAM/arena/latency figures are still
|
|
74
|
+
certain measurements, the coverage word honestly is not, and
|
|
75
|
+
`docs/model-check-static-screen.md` documents this exact combination as
|
|
76
|
+
legitimate. The one way `npu_coverage` still reaches `"fits"` at `basis:
|
|
77
|
+
"bench"` is DECISION 1's agreement path below: the customer's own `--exact`
|
|
78
|
+
compile already measured `"fits"` for real, the bench point corroborates it
|
|
79
|
+
on arena/SRAM, and the rebased report inherits that real, `--exact`-measured
|
|
80
|
+
verdict rather than a bench-authored one.
|
|
81
|
+
"""
|
|
82
|
+
from __future__ import annotations
|
|
83
|
+
|
|
84
|
+
import hashlib
|
|
85
|
+
from dataclasses import replace
|
|
86
|
+
from pathlib import Path
|
|
87
|
+
|
|
88
|
+
from .analyze import COVERAGE_WITHHELD, BackendReport, OpVerdict
|
|
89
|
+
from .perf import PerfPoint, coverage_from_placement, find_perf_points
|
|
90
|
+
from .targets import TargetSpec
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _placement_outcome(report: BackendReport, npu_ops: int | None,
|
|
94
|
+
cpu_ops: int | None) -> tuple[str, list[OpVerdict], int] | None:
|
|
95
|
+
"""(npu_coverage, ops, uncosted_cpu_op_count) for a MEASURED placement, or
|
|
96
|
+
`None` when there is no readable placement at all.
|
|
97
|
+
|
|
98
|
+
100% NPU is the only case that reports `"fits"`, and it drops `ops`: no
|
|
99
|
+
per-op verdict can add anything to "every operator ran on the NPU".
|
|
100
|
+
Partial or 0% KEEPS @report's static per-op verdicts AND its
|
|
101
|
+
uncosted-cpu-op count -- a real partial/0% result must erase neither
|
|
102
|
+
(MINOR review).
|
|
103
|
+
|
|
104
|
+
Takes the two COUNTS rather than a `Blob`: today's ONLY caller is
|
|
105
|
+
`tan.model.check._report_from_vela_compile`, off a real vela compile's own
|
|
106
|
+
"CPU/NPU operators = N" summary. It is NOT called for a bench perf point
|
|
107
|
+
any more (tan-cli#1115) -- alp-sdk's real schema carries no per-operator
|
|
108
|
+
placement counts on a published point, so there is nothing to feed this
|
|
109
|
+
with on that path; see this module's own docstring, "A BENCH POINT CANNOT
|
|
110
|
+
CLAIM A COVERAGE VERDICT". The coverage word itself comes from
|
|
111
|
+
`tan.model.perf.coverage_from_placement` -- the one function in tan
|
|
112
|
+
allowed to return `"fits"`."""
|
|
113
|
+
coverage = coverage_from_placement(npu_ops, cpu_ops)
|
|
114
|
+
if coverage is None:
|
|
115
|
+
return None
|
|
116
|
+
if coverage == "fits":
|
|
117
|
+
return coverage, [], 0
|
|
118
|
+
return coverage, report.ops, report.uncosted_cpu_op_count
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
#: backend -> the leading token of the REAL `target.compiler_version` string
|
|
122
|
+
#: that backend's compile adapter actually writes into a `.alpmodel` manifest
|
|
123
|
+
#: (and, by the same vocabulary, into a bench point -- `model-perf-v1.schema.
|
|
124
|
+
#: json`'s own description pins `compiler_version` to that manifest field).
|
|
125
|
+
#: Measured directly against alp-sdk's producers, not the op-support-table
|
|
126
|
+
#: filename vocabulary (`npu_ops/drpai/onnx-i8@translator-1.12.json`'s
|
|
127
|
+
#: `translator` is a DIFFERENT namespace -- a table-authoring convention, not
|
|
128
|
+
#: a compiler-identity string -- and an earlier version of this dict
|
|
129
|
+
#: conflated the two, matching NEITHER of `drpai`/`deepx_dxm1`'s real points:
|
|
130
|
+
#: tan-cli#1115 PR review, the issue's own failure mode one level down):
|
|
131
|
+
#:
|
|
132
|
+
#: ethos_u `_vela_version()` "vela {version}" / "vela"
|
|
133
|
+
#: (ethos_u.py:21-24)
|
|
134
|
+
#: drpai `_compiler_version()` "drp-ai_tvm {version}" / "drp-ai_tvm"
|
|
135
|
+
#: (drpai.py:96-110)
|
|
136
|
+
#: deepx_dxm1 `_dxcom_version()` "DX-COM {version}" / "dxcom" (bare,
|
|
137
|
+
#: (deepx.py:38-45) differently-spelled fallback -- see
|
|
138
|
+
#: `tan.model.perf._toolchain_name`'s
|
|
139
|
+
#: own docstring for why that spelling
|
|
140
|
+
#: is not chased)
|
|
141
|
+
#:
|
|
142
|
+
#: Compared case-foldedly by `find_perf_points` (alp-sdk's three real
|
|
143
|
+
#: producers do not agree on case -- `vela`/`drp-ai_tvm` lowercase, `DX-COM`
|
|
144
|
+
#: upper -- and case is not part of a measurement's identity), so this dict's
|
|
145
|
+
#: own values only need to be A correctly-spelled casing, not the specific
|
|
146
|
+
#: one a given point happens to carry; `test_check.py`'s
|
|
147
|
+
#: `test_perf_toolchain_matches_the_real_producer_strings` pins all three
|
|
148
|
+
#: against these cited literal format strings.
|
|
149
|
+
#:
|
|
150
|
+
#: `cpu` is deliberately ABSENT rather than mapped to a made-up name: a
|
|
151
|
+
#: backend with no entry here is simply never asked for a point, which is the
|
|
152
|
+
#: right behaviour for one nobody benches. `cpu` STAYS absent on purpose
|
|
153
|
+
#: (tan-cli#791 review, item 6): the schema's `target.backend` enum includes
|
|
154
|
+
#: it, so alp-sdk COULD publish a CPU-baseline campaign, but
|
|
155
|
+
#: `resolve_check_backends` (`tan.model.check`) already excludes `cpu` from
|
|
156
|
+
#: what `tan model check` screens at all -- "how much of this model can
|
|
157
|
+
#: target the NPU" has nothing to say about the fallback CPU path itself, and
|
|
158
|
+
#: that decision predates tier 2. Wiring `cpu` in here without also
|
|
159
|
+
#: revisiting that exclusion would publish points this reader can never be
|
|
160
|
+
#: asked for, which is worse than not wiring it: a campaign planner who greps
|
|
161
|
+
#: this dict for "which backends does tier 2 read" gets a true, complete
|
|
162
|
+
#: answer as of THIS decision, made here rather than rediscovered from a
|
|
163
|
+
#: silent no-op.
|
|
164
|
+
_PERF_TOOLCHAIN = {"ethos_u": "vela", "drpai": "drp-ai_tvm", "deepx_dxm1": "DX-COM"}
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _model_sha256(source: Path) -> str:
|
|
168
|
+
"""Lowercase sha256 of @source's exact bytes -- the perf-point match key.
|
|
169
|
+
|
|
170
|
+
Chunked, because a real model is not a small file (a production ONNX runs
|
|
171
|
+
to tens of MB), and computed ONCE per `check` rather than once per backend.
|
|
172
|
+
Any `OSError` propagates exactly as `extract_ops`' own read of the same file
|
|
173
|
+
does: an unreadable source is `model.check-failed`, never a quietly degraded
|
|
174
|
+
report."""
|
|
175
|
+
digest = hashlib.sha256()
|
|
176
|
+
with source.open("rb") as handle:
|
|
177
|
+
for chunk in iter(lambda: handle.read(1024 * 1024), b""):
|
|
178
|
+
digest.update(chunk)
|
|
179
|
+
return digest.hexdigest()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _resolve_hw_rev(sku: str, metadata_root: Path, declared: str | None) -> str | None:
|
|
183
|
+
"""The module revision a perf point must have been measured on: the
|
|
184
|
+
customer's own `board.yaml` `som.hw_rev` when they declared one, else the
|
|
185
|
+
SKU preset's `default_hw_rev`.
|
|
186
|
+
|
|
187
|
+
That fallback is the SDK's own rule, not an invention -- `board.schema.json`
|
|
188
|
+
says of `som.hw_rev`: "Optional; the SDK falls back to the SKU preset's
|
|
189
|
+
`default_hw_rev` when omitted" -- and both spellings use the same `rN` keys
|
|
190
|
+
the perf-point schema requires, so no translation is involved.
|
|
191
|
+
|
|
192
|
+
`None` when neither answers, and `find_perf_points` matches NOTHING on a
|
|
193
|
+
`None`: an r2 measurement handed to a customer holding an r1 module is
|
|
194
|
+
exactly the "exactly measured, describes a different machine" failure that
|
|
195
|
+
put `hw_rev` in a point's identity in the first place (alp-sdk `f724d3e4`).
|
|
196
|
+
Soft-fail on a preset that does not exist or does not parse, matching
|
|
197
|
+
`resolve_ethos_u_variant`'s stance next door.
|
|
198
|
+
|
|
199
|
+
tan-cli#1116: NOT a pre-flight `preset_path.is_file()` -- that used to
|
|
200
|
+
gate the read below, and on Python <= 3.13 `Path.is_file()` RAISES for a
|
|
201
|
+
permission-denied ancestor -- `PermissionError`, `errno=13`, measured on
|
|
202
|
+
both 3.12.3 and 3.13.15 -- swallowing only `ENOENT`/`ENOTDIR`/`EBADF`/
|
|
203
|
+
`ELOOP` there. `pathlib`'s `_IGNORED_ERRNOS` CONSTANT that names those
|
|
204
|
+
four is itself gone a release earlier (3.13, `AttributeError` probing
|
|
205
|
+
for it), but the raising BEHAVIOUR it used to name outlives the constant
|
|
206
|
+
by one release and only actually changes in 3.14: there `is_file()`
|
|
207
|
+
swallows EVERY `OSError`, `EACCES` included, and returns `False`
|
|
208
|
+
instead (tan-cli#1116 review round 2 -- a pre-flight guard is dead on
|
|
209
|
+
3.14+ regardless of what it catches, and reasoning from either "the
|
|
210
|
+
constant still exists" or "I measured the raise" picks the wrong
|
|
211
|
+
boundary, since the two diverge for exactly the 3.13 release). NOT
|
|
212
|
+
`EACCES`, so a
|
|
213
|
+
preset whose CONTAINING directory the caller cannot traverse raised a raw
|
|
214
|
+
`PermissionError` right there, past this function's own "Soft-fail"
|
|
215
|
+
contract -- the exact `document_guards.read_catalog_document` trap ("not
|
|
216
|
+
a pre-flight `is_file()`"), just via `is_file()` instead of
|
|
217
|
+
`Path.exists()`. Reading straight through the `try` below answers the
|
|
218
|
+
same three cases (absent, a directory, no permission) through `OSError`
|
|
219
|
+
alone -- on EVERY supported interpreter, since it never calls `is_file()`
|
|
220
|
+
at all -- so the pre-flight added nothing a plain `FileNotFoundError`
|
|
221
|
+
from the read did not already give it. `UnicodeDecodeError` is also now
|
|
222
|
+
caught explicitly: it is a `ValueError`, not an `OSError` and not a
|
|
223
|
+
`yaml.YAMLError`, so a non-UTF-8 preset used to escape raw past this same
|
|
224
|
+
contract (measured, tan-cli#1116).
|
|
225
|
+
|
|
226
|
+
`declared` reaching here as `None` is itself a FAIL-CLOSED answer from the
|
|
227
|
+
caller, not a "customer said nothing" default: `tan.commands.model_cmd.
|
|
228
|
+
_declared_hw_rev` refuses the whole run outright (a `model.board-yaml-
|
|
229
|
+
invalid` issue) when `som.hw_rev` is present but not a usable string --
|
|
230
|
+
e.g. an unquoted YAML int (`hw_rev: 2`) -- rather than letting it fall
|
|
231
|
+
through to here and silently pick up the preset's `default_hw_rev`
|
|
232
|
+
instead (tan-cli#791 review MINOR 5: measured `{'som': {'hw_rev': 2}} ->
|
|
233
|
+
None` serving an r2 preset measurement to a customer who wrote r2 but
|
|
234
|
+
would have been served r1's if the preset defaulted there)."""
|
|
235
|
+
if declared:
|
|
236
|
+
return declared
|
|
237
|
+
preset_path = Path(metadata_root) / "e1m_modules" / f"{sku}.yaml"
|
|
238
|
+
import yaml # noqa: PLC0415 -- deferred (tan-cli#810); see sdk_cmd's `_releases_opener`
|
|
239
|
+
|
|
240
|
+
try:
|
|
241
|
+
preset = yaml.safe_load(preset_path.read_text(encoding="utf-8")) or {}
|
|
242
|
+
except (OSError, UnicodeDecodeError, yaml.YAMLError):
|
|
243
|
+
return None
|
|
244
|
+
value = preset.get("default_hw_rev") if isinstance(preset, dict) else None
|
|
245
|
+
return str(value) if value else None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _part_declares_a_profile(target: TargetSpec) -> bool:
|
|
249
|
+
"""Does the SoC spec that produced @target name a vela memory profile at
|
|
250
|
+
all -- ANY of `vela_memory_mode` / `vela_system_config` /
|
|
251
|
+
`vela_vendor_system_config`?
|
|
252
|
+
|
|
253
|
+
Mirrors `_profile_matches_the_part`'s own `stated` computation exactly,
|
|
254
|
+
because the two must agree: a part this says `True` about is one
|
|
255
|
+
`_resolve_perf_point` (below) now narrows on BEFORE the single-point
|
|
256
|
+
shortcut, and a part this says `False` about must not be narrowed on at
|
|
257
|
+
all -- narrowing unconditionally would turn "the part is silent" into
|
|
258
|
+
"reject every point", which is the opposite failure from the BLOCKER this
|
|
259
|
+
function exists to close (tan-cli#791 review item 1): a part that states
|
|
260
|
+
nothing has nothing for `_profile_matches_the_part` to ever agree with, so
|
|
261
|
+
an unconditional narrow would empty out even the correctly-profiled
|
|
262
|
+
ordinary case."""
|
|
263
|
+
system_config = target.vela_system_config or target.vela_vendor_system_config
|
|
264
|
+
return bool(target.vela_memory_mode) or bool(system_config)
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def _profile_matches_the_part(point: PerfPoint, target: TargetSpec) -> bool:
|
|
268
|
+
"""Was @point captured under the memory profile THIS part declares?
|
|
269
|
+
|
|
270
|
+
The tiebreak, and the only one applied: the SoC spec's own
|
|
271
|
+
`npu_toolchain.vela` block (`TargetSpec.vela_memory_mode` /
|
|
272
|
+
`vela_system_config` / `vela_vendor_system_config`) is what `tan model
|
|
273
|
+
build` compiles this part under, so a point captured under the same profile
|
|
274
|
+
is the one describing THIS module. Comparing on a silicon fact rather than
|
|
275
|
+
on recency or on file order is the whole difference between choosing and
|
|
276
|
+
guessing -- the collision that put the profile into a point's identity left
|
|
277
|
+
a DRAM-backed capture as the arbitrary survivor on a part with no DRAM.
|
|
278
|
+
|
|
279
|
+
A field the SoC spec does not declare is not compared: `system_config` is
|
|
280
|
+
withheld from `TargetSpec` for every part shipped today (the Alif names live
|
|
281
|
+
only in a proprietary `.ini`), so on those parts this narrows on the memory
|
|
282
|
+
mode alone. `False` whenever there is nothing to compare at all -- an
|
|
283
|
+
unnarrowable ambiguity must fall through, not resolve to "everything
|
|
284
|
+
matches". CALLERS must gate this on `_part_declares_a_profile` first
|
|
285
|
+
(above) when using it to REJECT rather than to TIEBREAK: this function
|
|
286
|
+
alone cannot distinguish "nothing declared" from "declared and disagrees",
|
|
287
|
+
and only the caller knows which failure mode it is currently guarding."""
|
|
288
|
+
system_config = target.vela_system_config or target.vela_vendor_system_config
|
|
289
|
+
checks = [(target.vela_memory_mode, point.vela_memory_mode),
|
|
290
|
+
(system_config, point.vela_system_config)]
|
|
291
|
+
stated = [(declared, measured) for declared, measured in checks if declared]
|
|
292
|
+
return bool(stated) and all(declared == measured for declared, measured in stated)
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
def _topology_core_ids(sku: str, metadata_root: Path) -> set[str]:
|
|
296
|
+
"""Every core id @sku's OWN SoM preset `topology:` map declares -- the
|
|
297
|
+
physical cores this module actually has, keyed exactly as a perf point's
|
|
298
|
+
`target.core` must be (`metadata/schemas/model-perf-v1.schema.json`'s own
|
|
299
|
+
`target.core` description: "The SoM `topology:` role ... that issued this
|
|
300
|
+
measurement -- the core running the inference call, whether or not it
|
|
301
|
+
also owns the NPU doing the compute").
|
|
302
|
+
|
|
303
|
+
LEVEL 1 of the core check (tan-cli#791 round-2 review item 1). Applied
|
|
304
|
+
UNCONDITIONALLY, for every backend, regardless of whether the exact-match
|
|
305
|
+
query (`_resolve_perf_point`, below) already narrowed `core` to the
|
|
306
|
+
accelerator's own declared `paired_core`: a point naming a core the die
|
|
307
|
+
does not physically have is wrong either way. This is the check the
|
|
308
|
+
BLOCKER fix (the profile tiebreak, `_part_declares_a_profile`) left
|
|
309
|
+
undone, and it does not depend on any accelerator declaring a pairing at
|
|
310
|
+
all -- unlike the query's own narrowing, which only ever fires for an
|
|
311
|
+
accelerator that names one. Measured through `tan model check` (the
|
|
312
|
+
production path) on a part whose accelerator declares no `paired_core`: a
|
|
313
|
+
point claiming `core: "m55_hp"` -- a core that part does not have -- was
|
|
314
|
+
consumed at `basis: "bench", confidence: "certain"` with no check ever
|
|
315
|
+
having looked at `core` at all, because there was no `paired_core` for the
|
|
316
|
+
query to narrow on either.
|
|
317
|
+
This function is what makes that check possible for EVERY backend and
|
|
318
|
+
EVERY SKU, not only the ones that happen to pair an NPU to a core: a
|
|
319
|
+
die's `topology:` is required and always populated (all eleven shipped
|
|
320
|
+
presets declare one), so `p.core in _topology_core_ids(...)` is always a
|
|
321
|
+
real, checkable fact -- unlike `paired_core`, which is genuinely absent
|
|
322
|
+
for some accelerators by design.
|
|
323
|
+
|
|
324
|
+
`UnicodeDecodeError` is caught explicitly (tan-cli#1116, measured
|
|
325
|
+
escaping before this fix): it is a `ValueError`, not an `OSError` and
|
|
326
|
+
not a `yaml.YAMLError`, so a non-UTF-8 preset used to raise raw past
|
|
327
|
+
this function's own "refuses [to] `set()`" contract, same shape as
|
|
328
|
+
`_resolve_hw_rev`'s sibling fix next door.
|
|
329
|
+
|
|
330
|
+
By the time this runs, @sku's preset is already known to exist and parse
|
|
331
|
+
(`resolve_check_backends` -> `resolve_targets` succeeded before a single
|
|
332
|
+
backend was screened, reading the SAME file), so this re-reads it rather
|
|
333
|
+
than threading a parsed doc all the way down here, matching
|
|
334
|
+
`_resolve_hw_rev`'s own stance next door. `set()` -- which REFUSES every
|
|
335
|
+
point, never admits one -- only for a preset that dropped `topology:`
|
|
336
|
+
entirely or left it malformed, which no shipped SoM does: a core this
|
|
337
|
+
function cannot vouch for is not proof the SKU has it, the same "absent
|
|
338
|
+
means refuse" reading `find_perf_points` already gives a `None` `hw_rev`
|
|
339
|
+
(`PerfPoint.hw_rev` is REQUIRED and non-empty, so it can never equal an
|
|
340
|
+
unresolvable query value either)."""
|
|
341
|
+
preset_path = Path(metadata_root) / "e1m_modules" / f"{sku}.yaml"
|
|
342
|
+
import yaml # noqa: PLC0415 -- deferred (tan-cli#810); see sdk_cmd's `_releases_opener`
|
|
343
|
+
|
|
344
|
+
try:
|
|
345
|
+
preset = yaml.safe_load(preset_path.read_text(encoding="utf-8")) or {}
|
|
346
|
+
except (OSError, UnicodeDecodeError, yaml.YAMLError):
|
|
347
|
+
return set()
|
|
348
|
+
topology = preset.get("topology") if isinstance(preset, dict) else None
|
|
349
|
+
return set(topology) if isinstance(topology, dict) else set()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _one_line(text: str) -> str:
|
|
353
|
+
"""@text with every run of whitespace collapsed to a single space.
|
|
354
|
+
|
|
355
|
+
Applied to every FOREIGN string a perf-point note quotes (the capture
|
|
356
|
+
citation, the toolchain version, the hardware revision): a note is one line
|
|
357
|
+
in both the text report and the JSON envelope by contract, and these come
|
|
358
|
+
out of a document in ANOTHER repository that this reader deliberately does
|
|
359
|
+
not schema-validate."""
|
|
360
|
+
return " ".join(text.split())
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _perf_measured_clauses(point: PerfPoint) -> list[str]:
|
|
364
|
+
"""The `perf` figures @point actually carries, as note clauses.
|
|
365
|
+
|
|
366
|
+
A figure the point omits produces NO clause -- it was not measured, and
|
|
367
|
+
the note must not imply a zero. `latency_runs` travels with the latency
|
|
368
|
+
for the same reason the schema requires it: a latency without a run count
|
|
369
|
+
is a single shot wearing a measurement's clothes. NO operator-placement
|
|
370
|
+
clause: alp-sdk's real schema carries no per-op NPU/CPU split (tan-cli
|
|
371
|
+
#1115) -- see this module's own docstring, "A BENCH POINT CANNOT CLAIM A
|
|
372
|
+
COVERAGE VERDICT" -- so there is nothing sourced to phrase one from."""
|
|
373
|
+
clauses = []
|
|
374
|
+
if point.arena_bytes is not None:
|
|
375
|
+
clauses.append(f"arena {point.arena_bytes} bytes")
|
|
376
|
+
if point.req_sram_kib is not None:
|
|
377
|
+
clauses.append(f"{point.req_sram_kib} KiB resident SRAM")
|
|
378
|
+
if point.latency_ms_mean is not None and point.latency_runs is not None:
|
|
379
|
+
latency = f"{point.latency_ms_mean} ms mean"
|
|
380
|
+
if point.latency_ms_p95 is not None:
|
|
381
|
+
latency += f", {point.latency_ms_p95} ms p95"
|
|
382
|
+
clauses.append(f"{latency} over {point.latency_runs} runs")
|
|
383
|
+
return clauses
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
def _perf_identity(point: PerfPoint) -> str:
|
|
387
|
+
"""The measurement identity, as one readable clause -- module, hardware
|
|
388
|
+
revision, driving core, accelerator target, the exact compiler build, and
|
|
389
|
+
the memory profile the figures describe. Every one of them is in the note
|
|
390
|
+
because every one of them CHANGES THE NUMBER (alp-sdk `f724d3e4`), and a
|
|
391
|
+
customer sizing hardware has to be able to see which machine was measured.
|
|
392
|
+
|
|
393
|
+
The CORE is the one the match rule may leave unnarrowed -- where the
|
|
394
|
+
SPECIFIC accelerator being screened declares no `paired_core` of its own
|
|
395
|
+
(every drpai/deepx_dxm1 SoM; and the
|
|
396
|
+
E8's Ethos-U85, genuinely shared silicon rather than core-private -- see
|
|
397
|
+
`_resolve_perf_point`) -- so stating it here is the only way the customer
|
|
398
|
+
learns which one this point actually describes. `compiler_version` is
|
|
399
|
+
always stated verbatim (e.g. `"vela 4.1.0"`): the tier-2 customer has no
|
|
400
|
+
local toolchain to compare it against, so the exact build this point was
|
|
401
|
+
captured under is the only fact they get. The PROFILE is there for the
|
|
402
|
+
reason the schema makes it required on an Ethos-U point at all: an arena
|
|
403
|
+
captured under a DRAM-backed profile is exactly measured and describes the
|
|
404
|
+
wrong machine, and hiding it would re-open on the reading side the
|
|
405
|
+
ambiguity the schema closed on the writing side.
|
|
406
|
+
|
|
407
|
+
THE PROFILE CLAUSE IS `ethos_u`-ONLY (tan-cli#791 round-2 review item 5).
|
|
408
|
+
`vela_system_config`/`vela_memory_mode` are only ever narrowed or
|
|
409
|
+
verified against silicon for `ethos_u` -- `_part_declares_a_profile`/
|
|
410
|
+
`_profile_matches_the_part` run only when `target is not None`, and
|
|
411
|
+
`target` is `None` for every OTHER backend by construction
|
|
412
|
+
(`tan.model.check.check_model_backends` only resolves one for
|
|
413
|
+
`backend == "ethos_u"`). Nothing sourced exists to check a drpai/
|
|
414
|
+
deepx_dxm1 point's profile fields against, which is a DEFENSIBLE gap --
|
|
415
|
+
there is no vela-shaped profile concept on those toolchains to check --
|
|
416
|
+
but printing whatever those two fields happen to hold as if it were this
|
|
417
|
+
point's verified memory profile is not: measured, a drpai point carrying
|
|
418
|
+
a stray Ethos-U85 `Ethos_U85_SYS_DRAM_Mid`/`Dedicated_Sram_384KB` pair
|
|
419
|
+
printed that nonsense verbatim into the customer-facing note. So this
|
|
420
|
+
clause is withheld outright for every backend but `ethos_u`, regardless
|
|
421
|
+
of what the point's own fields carry."""
|
|
422
|
+
target = point.accel_config or point.backend
|
|
423
|
+
profile = ("" if point.backend != "ethos_u" else
|
|
424
|
+
", ".join(_one_line(p) for p in
|
|
425
|
+
(point.vela_system_config, point.vela_memory_mode) if p))
|
|
426
|
+
return (f"{point.sku} {_one_line(point.hw_rev)} (core {_one_line(point.core)}) "
|
|
427
|
+
f"at {_one_line(target)} with {_one_line(point.compiler_version)}"
|
|
428
|
+
+ (f" ({profile})" if profile else ""))
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
def _perf_point_note(point: PerfPoint, *, corroborated: bool) -> str:
|
|
432
|
+
"""The one-line note a `basis: "bench"` report leads with.
|
|
433
|
+
|
|
434
|
+
Carries `capture.bench_id` verbatim (whitespace-collapsed) -- WHICH
|
|
435
|
+
physical bench/rig captured this point (e.g. `"e1m-aen-evk-01"`), not a
|
|
436
|
+
capture artefact's own citation (that mislabel, "raw capture", used to be
|
|
437
|
+
the note's own wording -- tan-cli#1115 PR review minor) -- because a
|
|
438
|
+
number nobody can trace back to a specific rig is not reproducible, and a
|
|
439
|
+
perf point is worth exactly what its reproducibility is. Closes by saying
|
|
440
|
+
that a local compile can differ -- true for every backend, unlike an
|
|
441
|
+
`--exact` suggestion, which would be wrong advice on the two
|
|
442
|
+
license-gated ones."""
|
|
443
|
+
tail = ("This host's own compile agrees with it." if corroborated else
|
|
444
|
+
"A compile on your own host can differ if its toolchain profile "
|
|
445
|
+
"differs from the one recorded here.")
|
|
446
|
+
return (f"bench: Alp Lab measured this exact model on {_perf_identity(point)} "
|
|
447
|
+
f"-- {'; '.join(_perf_measured_clauses(point))}. Captured "
|
|
448
|
+
f"{_one_line(point.capture_date)} on bench "
|
|
449
|
+
f"{_one_line(point.capture_bench_id)}. {tail}")
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
def _perf_disagreements(point: PerfPoint, report: BackendReport) -> list[str]:
|
|
453
|
+
"""Every concrete way @point and the customer's OWN `basis: "compiled"`
|
|
454
|
+
@report differ -- empty when they agree on everything both measured.
|
|
455
|
+
|
|
456
|
+
"Disagrees" is defined on the two figures BOTH sides really produce: the
|
|
457
|
+
tensor arena and the resident SRAM. NOT the placement verdict any more
|
|
458
|
+
(tan-cli#1115): alp-sdk's real schema carries no bench-measured
|
|
459
|
+
operator-placement split for a bench point to disagree about (see this
|
|
460
|
+
module's own docstring, "A BENCH POINT CANNOT CLAIM A COVERAGE VERDICT")
|
|
461
|
+
-- `report.npu_coverage` is a real `--exact` compile's OWN verdict either
|
|
462
|
+
way, so there is nothing left to compare it against. Latency has no
|
|
463
|
+
compiled counterpart to disagree with either, and a figure EITHER side
|
|
464
|
+
did not report is never a disagreement: absence is not evidence here any
|
|
465
|
+
more than anywhere else in this tier."""
|
|
466
|
+
diffs: list[str] = []
|
|
467
|
+
for label, unit, measured, compiled in (
|
|
468
|
+
("arena", "bytes", point.arena_bytes, report.arena_bytes),
|
|
469
|
+
("SRAM", "KiB", point.req_sram_kib, report.req_sram_kib)):
|
|
470
|
+
if measured is not None and compiled is not None and measured != compiled:
|
|
471
|
+
diffs.append(f"{label} (bench {measured} {unit}, local compile {compiled} {unit})")
|
|
472
|
+
return diffs
|
|
473
|
+
|
|
474
|
+
|
|
475
|
+
def _perf_disagreement_note(point: PerfPoint, diffs: list[str]) -> str:
|
|
476
|
+
"""Why the customer's own compile is being reported and OUR measurement is
|
|
477
|
+
not -- with the measurement still named, so nothing is hidden."""
|
|
478
|
+
return (f"--exact wins here: Alp Lab's bench point for this exact model on "
|
|
479
|
+
f"{_perf_identity(point)} ({_one_line(point.capture_bench_id)}) "
|
|
480
|
+
f"disagrees with this host's own compile -- {'; '.join(diffs)}. The "
|
|
481
|
+
f"LOCAL compile is what is reported above: it ran this host's own "
|
|
482
|
+
f"toolchain and profile, which is what a build here would produce.")
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
#: A note whose text starts with `"--exact"` is a diagnostic about THIS RUN's
|
|
486
|
+
#: HOST/toolchain environment for the customer's `--exact` request (vela
|
|
487
|
+
#: absent, license-gated, footprint refused, ...) -- every HOST-fact
|
|
488
|
+
#: note-authoring site in `tan.model.check`'s exact-compile path shares this
|
|
489
|
+
#: prefix by construction (`_license_gated_exact_note`, `_footprint_refused_
|
|
490
|
+
#: note`, and every degrade branch in `_exact_ethos_u`/`_vela_placement_
|
|
491
|
+
#: unreadable`). The BASIS-clause sites in that SAME path deliberately do
|
|
492
|
+
#: NOT share it -- "This stays the static screen."/"Reporting the static
|
|
493
|
+
#: screen instead." (`check.py:233`, `:341`, `:387`, `:429`, `:518`) -- and
|
|
494
|
+
#: that is by design, not an oversight: `_perf_point_report` (below) uses the
|
|
495
|
+
#: prefix to tell the two kinds of note apart. The former survives a re-base
|
|
496
|
+
#: onto a bench point because it is still true of this run; the latter does
|
|
497
|
+
#: not, because it describes a basis the report no longer has (tan-cli#791
|
|
498
|
+
#: review item 4: measured the "--exact was requested, but vela is not on
|
|
499
|
+
#: PATH" note present with no perf point and GONE the moment one matched).
|
|
500
|
+
_EXACT_HOST_NOTE_PREFIX = "--exact"
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
def _merge_figure(point_value, report_value):
|
|
504
|
+
"""THE MERGE RULE for every footprint/latency figure a rebase touches
|
|
505
|
+
(tan-cli#1115 PR review blocker 3): @point's own value when it has one,
|
|
506
|
+
else whatever @report already carried -- MOST-SPECIFIC-WINS, per field,
|
|
507
|
+
never a blanket "the point's block replaces the report's".
|
|
508
|
+
|
|
509
|
+
A matched bench point is not guaranteed to carry every figure (the
|
|
510
|
+
schema's `perf.latency_ms` is optional even where `req_sram_kib`/
|
|
511
|
+
`arena_bytes` are schema-required, and this reader stays defensive
|
|
512
|
+
against a malformed point missing even those -- see `tan.model.perf`'s
|
|
513
|
+
own `test_a_missing_or_malformed_perf_block_still_reads_the_identity`),
|
|
514
|
+
so a rebase that unconditionally overwrote every figure with @point's
|
|
515
|
+
(`None` included) blanked a REAL `--exact` compile's own measured
|
|
516
|
+
`arenaBytes`/`reqSramKib` the moment a matched point recorded only
|
|
517
|
+
latency -- reproduced: a compiled report's `arena_bytes=123456,
|
|
518
|
+
req_sram_kib=121` became `None, None` under `basis: "bench", confidence:
|
|
519
|
+
"certain"`, directly contradicting `apply_perf_point`'s own promise that
|
|
520
|
+
an under-informative point "cannot degrade a report by even a note".
|
|
521
|
+
Applied uniformly to every figure below, not only the two that were
|
|
522
|
+
reproduced: a bench point's latency is likewise never allowed to blank a
|
|
523
|
+
real compile's (today impossible in practice -- `_report_from_vela_
|
|
524
|
+
compile` never sets latency -- but the rule is stated once, for every
|
|
525
|
+
field, rather than fixed only at the two sites a repro happened to hit."""
|
|
526
|
+
return point_value if point_value is not None else report_value
|
|
527
|
+
|
|
528
|
+
|
|
529
|
+
def _perf_point_report(report: BackendReport, point: PerfPoint) -> BackendReport:
|
|
530
|
+
"""@report re-based on @point: `basis: "bench"`, `confidence: "certain"`.
|
|
531
|
+
|
|
532
|
+
Drops @report's BASIS-describing notes and leads with the bench note,
|
|
533
|
+
exactly as `tan.model.check._report_from_vela_compile` does for a real
|
|
534
|
+
compile -- those notes describe a basis this report no longer has. Notes
|
|
535
|
+
that instead describe the HOST/toolchain environment for a `--exact`
|
|
536
|
+
request (`_EXACT_HOST_NOTE_PREFIX`) are still true of this run and are
|
|
537
|
+
KEPT, appended after the bench note (tan-cli#791 review item 4).
|
|
538
|
+
|
|
539
|
+
Two rules, both spelled out in full in this module's own docstring
|
|
540
|
+
("A BENCH POINT CANNOT CLAIM A COVERAGE VERDICT" -- read that first):
|
|
541
|
+
footprint/latency figures merge PER FIELD, most-specific-wins
|
|
542
|
+
(`_merge_figure`, PR review blocker 3); coverage/placement fields carry
|
|
543
|
+
from @report ONLY when `report.basis == "compiled"`, else `npu_coverage`
|
|
544
|
+
is withheld to `"undetermined"` (PR review blocker 1). `compute_on_npu_
|
|
545
|
+
pct_max` is FORCED to `None` regardless -- `analyze.BackendReport`'s own
|
|
546
|
+
field comment: that MAC-weighted figure is "ONLY at basis: static-screen",
|
|
547
|
+
same reason it is already `None` at `basis: "compiled"`.
|
|
548
|
+
|
|
549
|
+
`sram_fit` (tan-cli#1288) rides the SAME `real_placement` gate as
|
|
550
|
+
`ops`/`npu_placement_pct_real`: it is a verdict about the COMPILED blob a
|
|
551
|
+
real `--exact` run just produced, not something a bare bench point can
|
|
552
|
+
independently corroborate (alp-sdk's `model_perf/` schema carries no
|
|
553
|
+
SRAM0-residency figure of its own, tan-cli#1115's "DEAD FIELDS"), so it
|
|
554
|
+
only survives the rebase when this report was ALREADY `basis: "compiled"`
|
|
555
|
+
-- otherwise it is dropped to `None`, exactly like `ops`'s own
|
|
556
|
+
withhold-when-not-corroborated shape."""
|
|
557
|
+
host_notes = [n for n in report.notes if n.startswith(_EXACT_HOST_NOTE_PREFIX)]
|
|
558
|
+
real_placement = report.basis == "compiled"
|
|
559
|
+
return BackendReport(
|
|
560
|
+
backend=report.backend, variant=report.variant, table=report.table,
|
|
561
|
+
npu_coverage=report.npu_coverage if real_placement else COVERAGE_WITHHELD,
|
|
562
|
+
compute_on_npu_pct_max=None,
|
|
563
|
+
npu_placement_pct_real=report.npu_placement_pct_real if real_placement else None,
|
|
564
|
+
uncosted_cpu_op_count=report.uncosted_cpu_op_count if real_placement else 0,
|
|
565
|
+
ops=report.ops if real_placement else [],
|
|
566
|
+
basis="bench", confidence="certain",
|
|
567
|
+
arena_bytes=_merge_figure(point.arena_bytes, report.arena_bytes),
|
|
568
|
+
req_sram_kib=_merge_figure(point.req_sram_kib, report.req_sram_kib),
|
|
569
|
+
latency_ms_mean=_merge_figure(point.latency_ms_mean, report.latency_ms_mean),
|
|
570
|
+
latency_ms_p95=_merge_figure(point.latency_ms_p95, report.latency_ms_p95),
|
|
571
|
+
latency_runs=_merge_figure(point.latency_runs, report.latency_runs),
|
|
572
|
+
perf_ref=point.capture_bench_id,
|
|
573
|
+
notes=[_perf_point_note(point, corroborated=report.basis == "compiled"), *host_notes],
|
|
574
|
+
sram_fit=report.sram_fit if real_placement else None,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
def _resolve_perf_point(*, backend: str, sku: str, model_sha256: str,
|
|
579
|
+
hw_rev: str | None, metadata_root: Path,
|
|
580
|
+
target: TargetSpec | None) -> PerfPoint | None:
|
|
581
|
+
"""The ONE point that describes this customer's module, or `None`.
|
|
582
|
+
|
|
583
|
+
@target is the ALREADY-RESOLVED Ethos-U accelerator
|
|
584
|
+
(`tan.model.check._headline_ethos_u_target`'s answer for @backend ==
|
|
585
|
+
`"ethos_u"`, `None` for every other backend) -- passed in rather than
|
|
586
|
+
resolved here so this module never has to import `check.py` back.
|
|
587
|
+
|
|
588
|
+
Narrows on everything tan can SOURCE: the SKU, the module revision, the
|
|
589
|
+
accel config `--exact` would compile for, the model's own bytes, and the
|
|
590
|
+
toolchain name this backend's points are keyed by. It does NOT narrow on
|
|
591
|
+
the toolchain VERSION -- there is no local toolchain to read one off in the
|
|
592
|
+
tier-2 premise, and a point measured at another version is still OUR
|
|
593
|
+
measurement (decision 1).
|
|
594
|
+
|
|
595
|
+
CORE narrows in TWO WAYS, and both are DECLARED facts -- never an
|
|
596
|
+
inference borrowed from elsewhere on the die (tan-cli#791 round-3,
|
|
597
|
+
driven by newly-sourced silicon evidence; see below). The exact-match
|
|
598
|
+
query below narrows to @target's OWN `paired_core` when @target declares
|
|
599
|
+
one -- the case for every Ethos-U55 published today, including both of
|
|
600
|
+
the E8's (`m55_hp`/`m55_he`): that pairing is a fact about the SAME
|
|
601
|
+
accelerator being screened, so enforcing it is sound. LEVEL 1
|
|
602
|
+
(`_topology_core_ids`), applied UNCONDITIONALLY for every backend
|
|
603
|
+
regardless of what @target declares: a point's core must exist in @sku's
|
|
604
|
+
OWN `topology:` map at all, full stop -- this is what catches a point
|
|
605
|
+
claiming `core: "m55_hp"` on a SoM whose topology has no such core
|
|
606
|
+
or any other core nothing on the module
|
|
607
|
+
has ever had.
|
|
608
|
+
|
|
609
|
+
WHERE @target DECLARES NO `paired_core` OF ITS OWN, core narrowing STOPS
|
|
610
|
+
at level 1 -- tan infers NOTHING further, and in particular never borrows
|
|
611
|
+
a SIBLING NPU's pairing on the same die. A prior round of this reader did
|
|
612
|
+
exactly that: it took the UNION of `paired_core` across every NPU of
|
|
613
|
+
@backend on the die and applied it even to an accelerator that named no
|
|
614
|
+
pairing of its own, on the reasoning that the E8's Ethos-U85 (unpaired)
|
|
615
|
+
shares a die with two Ethos-U55s (paired to `m55_hp`/`m55_he`), so a point
|
|
616
|
+
naming `a32_cluster` for the U85 "must" be wrong. Newly-sourced silicon
|
|
617
|
+
evidence proves that inference wrong: the U85's `NPU_HG_BASE` register
|
|
618
|
+
alias is byte-identical across BOTH M55 cores' generated headers, its
|
|
619
|
+
vendor DFP `<feature>` element carries no `Pname` at all (unlike the two
|
|
620
|
+
Ethos-U55s' `Pname="M55_HP"`/`"M55_HE"`), and it is clock-gated
|
|
621
|
+
SYSTEM-side (`SHMEM_CLK_CTRL.ZAPHOD_CKOR`) rather than from either core's
|
|
622
|
+
own config block -- it is genuinely shared SoC-level silicon, not a
|
|
623
|
+
core-private accelerator that merely forgot to declare its pairing. alp-
|
|
624
|
+
sdk's own `soc-spec-v1.schema.json` says exactly that is legitimate:
|
|
625
|
+
`paired_core`'s own field description says to "Omit for a shared /
|
|
626
|
+
non-core-paired NPU". A point naming ANY core @sku's own topology admits
|
|
627
|
+
(level 1) is therefore accepted for such an accelerator, `a32_cluster`
|
|
628
|
+
included -- accepting it is not a claim that the U85 CAN be driven from
|
|
629
|
+
the A32 cluster, only that the customer's own report of what they ran is
|
|
630
|
+
not contradicted by any sourced fact. Nothing sourced says it cannot, either:
|
|
631
|
+
there is no A32 SVD view and no A32 board devicetree NPU node to check
|
|
632
|
+
either way, so refusing the core would have been an equally unsourced
|
|
633
|
+
assertion in the OTHER direction. Only where @target declares no pairing
|
|
634
|
+
at all -- e.g. every drpai/deepx_dxm1 SoM today -- does level 1 alone
|
|
635
|
+
decide, exactly as it always did.
|
|
636
|
+
|
|
637
|
+
THE PROFILE TIEBREAK NARROWS BEFORE THE SINGLE-POINT SHORTCUT, not after
|
|
638
|
+
(tan-cli#791 review item 1, the BLOCKER). A part that declares a vela
|
|
639
|
+
memory profile (`_part_declares_a_profile`) only ever matches a point
|
|
640
|
+
captured under that SAME profile -- a lone point captured under a profile
|
|
641
|
+
the part does NOT declare (a DRAM-backed default measured against a
|
|
642
|
+
Sram_Only part) must be refused exactly as decisively as a second,
|
|
643
|
+
wrongly-profiled point in a multi-point tie already was: the old code
|
|
644
|
+
applied this ONLY once two or more points survived the sourced filters
|
|
645
|
+
above, so a single wrong-profile point was returned unfiltered at
|
|
646
|
+
`basis: "bench", confidence: "certain"` -- overstating resident SRAM by
|
|
647
|
+
5.3x in the measured case (a DRAM-backed default capture against
|
|
648
|
+
E8-class e8.json's `Sram_Only`-declaring part). Applied only where the
|
|
649
|
+
part actually states a profile: narrowing on a part that states nothing
|
|
650
|
+
would reject every point outright, the opposite failure.
|
|
651
|
+
|
|
652
|
+
THE MULTI-MATCH RULE, for what is left. More than one point can
|
|
653
|
+
legitimately still stand -- one model, one module, one core, one
|
|
654
|
+
toolchain, captured under two different vela profiles the PART itself
|
|
655
|
+
leaves unconstrained (`_part_declares_a_profile` false for it). Nothing
|
|
656
|
+
further is picked: still several, or none once narrowed, falls through to
|
|
657
|
+
the next fidelity tier, silently, rather than guessing."""
|
|
658
|
+
toolchain = _PERF_TOOLCHAIN.get(backend)
|
|
659
|
+
if toolchain is None:
|
|
660
|
+
return None
|
|
661
|
+
if backend == "ethos_u" and target is None:
|
|
662
|
+
return None
|
|
663
|
+
points = find_perf_points(
|
|
664
|
+
sku=sku, backend=backend, accel_config=target.accel_config if target else "",
|
|
665
|
+
model_sha256=model_sha256, toolchain=toolchain, hw_rev=hw_rev,
|
|
666
|
+
core=target.paired_core if target else None, metadata_root=metadata_root)
|
|
667
|
+
topology_cores = _topology_core_ids(sku, metadata_root)
|
|
668
|
+
points = [p for p in points if p.core in topology_cores]
|
|
669
|
+
if target is not None and _part_declares_a_profile(target):
|
|
670
|
+
points = [p for p in points if _profile_matches_the_part(p, target)]
|
|
671
|
+
return points[0] if len(points) == 1 else None
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def apply_perf_point(report: BackendReport, *, backend: str, sku: str,
|
|
675
|
+
model_sha256: str, hw_rev: str | None,
|
|
676
|
+
metadata_root: Path, target: TargetSpec | None) -> BackendReport:
|
|
677
|
+
"""Tier 2, or @report untouched.
|
|
678
|
+
|
|
679
|
+
Every early return hands back the SAME object, with no exception: an
|
|
680
|
+
absent, ambiguous, fixture-marked, or empty point cannot degrade a report
|
|
681
|
+
by even a note. The last of those is the subtle one: a point that carries
|
|
682
|
+
none of `arena_bytes`/`req_sram_kib`/`latency_ms_mean` -- schema-legal but
|
|
683
|
+
with nothing left for this function to actually contribute -- must not
|
|
684
|
+
re-base the report to `basis: "bench"` over an empty note; that would
|
|
685
|
+
claim a measurement said something it didn't.
|
|
686
|
+
|
|
687
|
+
A MATCHED POINT NEVER DETERMINES `npu_coverage` ITSELF -- see this
|
|
688
|
+
module's own docstring ("A BENCH POINT CANNOT CLAIM A COVERAGE VERDICT")
|
|
689
|
+
and `_perf_point_report`'s for the full withhold-unless-`"compiled"` rule.
|
|
690
|
+
|
|
691
|
+
DECISION 1 (tier-2 plan, Task 4): a `basis: "compiled"` report -- the
|
|
692
|
+
customer really does hold the toolchain and really did compile -- WINS
|
|
693
|
+
over a disagreeing point, named in a note instead; their profile may not
|
|
694
|
+
be ours, and the number they can act on is the one their machine
|
|
695
|
+
produced. THE POINT'S OWN LATENCY AND CAPTURE CITATION STILL RIDE AS
|
|
696
|
+
FIELDS on that disagreement report (tan-cli#791 review item 3): a compile
|
|
697
|
+
has no wall-clock or raw-capture counterpart to disagree WITH, so they
|
|
698
|
+
are never part of `_perf_disagreements`' comparison, only offered
|
|
699
|
+
alongside the winning LOCAL numbers -- without this `perfRef` was `null`
|
|
700
|
+
in the same envelope whose own note prose quoted it.
|
|
701
|
+
|
|
702
|
+
When the two AGREE the point wins, because it adds measured wall-clock
|
|
703
|
+
latency and a traceable capture no compile can produce, and the local
|
|
704
|
+
compile corroborating it is said out loud."""
|
|
705
|
+
point = _resolve_perf_point(backend=backend, sku=sku, model_sha256=model_sha256,
|
|
706
|
+
hw_rev=hw_rev, metadata_root=metadata_root, target=target)
|
|
707
|
+
if point is None:
|
|
708
|
+
return report
|
|
709
|
+
if (point.arena_bytes is None and point.req_sram_kib is None
|
|
710
|
+
and point.latency_ms_mean is None):
|
|
711
|
+
return report
|
|
712
|
+
if report.basis == "compiled":
|
|
713
|
+
diffs = _perf_disagreements(point, report)
|
|
714
|
+
if diffs:
|
|
715
|
+
return replace(
|
|
716
|
+
report, notes=[*report.notes, _perf_disagreement_note(point, diffs)],
|
|
717
|
+
perf_ref=point.capture_bench_id,
|
|
718
|
+
latency_ms_mean=point.latency_ms_mean,
|
|
719
|
+
latency_ms_p95=point.latency_ms_p95,
|
|
720
|
+
latency_runs=point.latency_runs)
|
|
721
|
+
return _perf_point_report(report, point)
|