-
Notifications
You must be signed in to change notification settings - Fork 1k
NXP backend: Improve test result gathering #20024
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,8 +9,9 @@ | |
| # | ||
|
|
||
| import logging | ||
| import os | ||
| import struct | ||
| from typing import final, List, Optional | ||
| from typing import final | ||
|
|
||
| import numpy as np | ||
| import torch | ||
|
|
@@ -45,10 +46,11 @@ class NeutronCompileSpecBuilder: | |
| config: NeutronTargetSpec | ||
|
|
||
| def __init__(self): | ||
| self.compile_spec: List[CompileSpec] = [] | ||
| self.compile_spec: list[CompileSpec] = [] | ||
| self.compiler_flags = [] | ||
| self.output_format = None | ||
| self.operators_not_to_delegate: List[str] = [] | ||
| self.test_dir = None | ||
| self.operators_not_to_delegate: list[str] = [] | ||
| self.use_neutron_for_format_conversion = True | ||
| self.fetch_constants_to_sram = False | ||
| self.dump_kernel_selection_code = False | ||
|
|
@@ -62,15 +64,17 @@ def _replace_colons(self, operator: str) -> str: | |
| def neutron_compile_spec( | ||
| self, | ||
| config: str, | ||
| extra_flags: Optional[str] = None, | ||
| operators_not_to_delegate: Optional[List[str]] = None, | ||
| test_dir: str | None = None, | ||
| extra_flags: str | None = None, | ||
| operators_not_to_delegate: list[str] | None = None, | ||
| use_neutron_for_format_conversion: bool = True, | ||
| fetch_constants_to_sram: bool = False, | ||
| dump_kernel_selection_code: bool = False, | ||
| ) -> "NeutronCompileSpecBuilder": | ||
| """Generate compile spec for Neutron NPU | ||
|
|
||
| :param config: Neutron accelerator configuration, e.g. "imxrt700" | ||
| :param test_dir: Test directory to store test related files. | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The intermediate models are related to the test. They are stored to the test directory where other test related data are store as well (results, datasets, etc.) Don't see a point in different naming.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "The intermediate models are related to the test." "They are stored to the test directory where other test related data are store as well (results, datasets, etc.)" |
||
| :param extra_flags: Extra flags for the Neutron compiler | ||
| :param operators_not_to_delegate: List of operators that should not be delegated | ||
| :param use_neutron_for_format_conversion: If True, the EdgeProgramToIRConverter will insert `Transpose` ops to | ||
|
|
@@ -83,6 +87,7 @@ def neutron_compile_spec( | |
| """ | ||
|
|
||
| self.config = NeutronTargetSpec(config) | ||
| self.test_dir = test_dir if test_dir is not None else os.getcwd() | ||
|
|
||
| assert ( | ||
| self.output_format is None | ||
|
|
@@ -113,6 +118,7 @@ def build(self): | |
| CompileSpec("output_format", "tflite".encode()), | ||
| CompileSpec("compile_flags", " ".join(self.compiler_flags).encode()), | ||
| CompileSpec("target", self.config.get_name().encode()), | ||
| CompileSpec("test_dir", f"{self.test_dir}".encode()), | ||
| CompileSpec( | ||
| "operators_not_to_delegate", | ||
| ",".join(self.operators_not_to_delegate).encode(), | ||
|
|
@@ -136,17 +142,19 @@ def build(self): | |
|
|
||
| def generate_neutron_compile_spec( | ||
| config: str, # The target platform. For example "imxrt700". | ||
| system_config: Optional[str] = None, | ||
| extra_flags: Optional[str] = None, | ||
| operators_not_to_delegate: Optional[List[str]] = None, | ||
| system_config: str | None = None, | ||
| extra_flags: str | None = None, | ||
| test_dir: str | None = None, | ||
| operators_not_to_delegate: list[str] | None = None, | ||
| use_neutron_for_format_conversion: bool = True, | ||
| fetch_constants_to_sram: bool = False, | ||
| dump_kernel_selection_code: bool = False, | ||
| ) -> List[CompileSpec]: | ||
| ) -> list[CompileSpec]: | ||
| return ( | ||
| NeutronCompileSpecBuilder() | ||
| .neutron_compile_spec( | ||
| config, | ||
| test_dir=test_dir, | ||
| extra_flags=extra_flags, | ||
| operators_not_to_delegate=operators_not_to_delegate, | ||
| use_neutron_for_format_conversion=use_neutron_for_format_conversion, | ||
|
|
@@ -163,7 +171,7 @@ class NeutronBackend(BackendDetails): | |
| @staticmethod | ||
| def preprocess( # noqa C901 | ||
| edge_program: ExportedProgram, | ||
| compile_spec: List[CompileSpec], | ||
| compile_spec: list[CompileSpec], | ||
| ) -> PreprocessResult: | ||
| logging.info("NeutronBackend::preprocess") | ||
|
|
||
|
|
@@ -173,6 +181,7 @@ def preprocess( # noqa C901 | |
| compile_flags = [] | ||
| binary = bytes() | ||
| target = "" | ||
| test_dir = "" | ||
| use_neutron_for_format_conversion = None | ||
| fetch_constants_to_sram = False | ||
| dump_kernel_selection_code = None | ||
|
|
@@ -181,6 +190,8 @@ def preprocess( # noqa C901 | |
| output_format = spec.value.decode() | ||
| if spec.key == "target": | ||
| target = spec.value.decode() | ||
| if spec.key == "test_dir": | ||
| test_dir = spec.value.decode() | ||
| if spec.key == "compile_flags": | ||
| compile_flags.append(spec.value.decode()) | ||
| if spec.key == "use_neutron_for_format_conversion": | ||
|
|
@@ -230,14 +241,16 @@ def preprocess( # noqa C901 | |
|
|
||
| # Dump the tflite file if logging level is enabled | ||
| if logging.root.isEnabledFor(logging.DEBUG): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As now we already introduce the
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't understand. The functionality is (and was before) limited to DEBUG logging level only. The logic to check is
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It was limited to DEBUG level only as there was no other means to enable/disable it. Now you introduced a specific entry in the conversion_config, so you can use that. And not limit the intermediates dump only to DEBUG log level. |
||
| import os | ||
|
|
||
| logging.debug( | ||
| f"Serializing converted graph with tag {delegation_tag} to {os.getcwd()}" | ||
| f"Serializing converted graph with tag {delegation_tag} to {test_dir}" | ||
| ) | ||
| with open(f"{delegation_tag}_pure.et.tflite", "wb") as f: | ||
| with open( | ||
| os.path.join(test_dir, f"{delegation_tag}_pure.et.tflite"), "wb" | ||
| ) as f: | ||
| f.write(bytes(tflite_model)) | ||
| with open(f"{delegation_tag}_neutron.et.tflite", "wb") as f: | ||
| with open( | ||
| os.path.join(test_dir, f"{delegation_tag}_neutron.et.tflite"), "wb" | ||
| ) as f: | ||
| f.write(bytes(neutron_model)) | ||
|
|
||
| binary = PayloadComposer().get_binary_payload(io_formats, neutron_model) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| # Copyright 2026 NXP | ||
| # | ||
| # This source code is licensed under the BSD-style license found in the | ||
| # LICENSE file in the root directory of this source tree. | ||
|
|
||
| import logging | ||
| import os | ||
|
|
||
| import numpy as np | ||
| import pytest | ||
| import torch | ||
|
|
||
| from executorch.backends.nxp.tests.executorch_pipeline import ModelInputSpec | ||
| from executorch.backends.nxp.tests.graph_verifier import BaseGraphVerifier | ||
| from executorch.backends.nxp.tests.models import AddTensorModule, AvgPool2dModule | ||
| from executorch.backends.nxp.tests.nsys_testing import ( | ||
| get_test_name, | ||
| lower_run_compare, | ||
| OUTPUTS_DIR, | ||
| ) | ||
|
|
||
|
|
||
| @pytest.fixture(autouse=True) | ||
| def reseed_model_per_test_run(): | ||
| torch.manual_seed(23) | ||
| np.random.seed(23) | ||
|
|
||
|
|
||
| def test_nsys_test_debug_results__single_input(caplog, request): | ||
|
roman-janik-nxp marked this conversation as resolved.
|
||
| # Set log level to DEBUG to create debug results | ||
| caplog.set_level(logging.DEBUG) | ||
|
|
||
| input_shape = (2, 4, 6, 7) | ||
| model = AvgPool2dModule(False, 0) | ||
|
|
||
| graph_verifier = BaseGraphVerifier(1, []) | ||
|
|
||
| lower_run_compare( | ||
| model, | ||
| input_shape, | ||
| graph_verifier, | ||
| request, | ||
| remove_quant_io_ops=True, | ||
| ) | ||
|
|
||
| test_name = get_test_name(request) | ||
|
roman-janik-nxp marked this conversation as resolved.
|
||
| # Running by CI scripts adds prefix to the name | ||
| assert "test_nsys_test_debug_results__single_input" in test_name | ||
| assert os.path.isdir(os.path.join(OUTPUTS_DIR, test_name, "diff_cpu_npu_results")) | ||
| assert os.path.isfile(os.path.join(OUTPUTS_DIR, test_name, "summary.yaml")) | ||
|
roman-janik-nxp marked this conversation as resolved.
|
||
|
|
||
| # Check file contains key symbols | ||
| with open(os.path.join(OUTPUTS_DIR, test_name, "summary.yaml")) as f: | ||
| content = f.read() | ||
| keys = [ | ||
| "date_time", | ||
| "eiq_neutron_sdk_version", | ||
| "eiq_nsys_version", | ||
| "git_branch", | ||
| "git_commit", | ||
| "test_name", | ||
| ] | ||
| assert all(key in content for key in keys) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "tag1_neutron.et.tflite") | ||
| ) | ||
| assert os.path.isfile(os.path.join(OUTPUTS_DIR, test_name, "tag1_pure.et.tflite")) | ||
|
|
||
| # Check text tensor variants | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "dataset", "calibration", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "dataset_quant", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "results_cpu", "0000.bin", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "results_npu", "0000.bin", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join( | ||
| OUTPUTS_DIR, test_name, "diff_cpu_npu_results", "0000.bin", "0000.txt" | ||
| ) | ||
| ) | ||
| assert os.path.isfile(os.path.join(OUTPUTS_DIR, f"{test_name}.zip")) | ||
|
|
||
|
|
||
| class TestNsysDebugResults: | ||
| def test_nsys_test_debug_results__multiple_input(self, caplog, request): | ||
| # Set log level to DEBUG to create debug results | ||
| caplog.set_level(logging.DEBUG) | ||
|
|
||
| input_shape = (1, 4, 7) | ||
| x_input_spec = ModelInputSpec(input_shape) | ||
| model = AddTensorModule() | ||
|
|
||
| graph_verifier = BaseGraphVerifier(1, []) | ||
|
|
||
| lower_run_compare( | ||
| model, | ||
| [x_input_spec, x_input_spec], | ||
| graph_verifier, | ||
| request, | ||
| ) | ||
|
|
||
| test_name = get_test_name(request) | ||
| # Running by CI scripts adds prefix to the name | ||
| assert ( | ||
| "TestNsysDebugResults__test_nsys_test_debug_results__multiple_input" | ||
| in test_name | ||
| ) | ||
| assert os.path.isdir( | ||
| os.path.join(OUTPUTS_DIR, test_name, "diff_cpu_npu_results") | ||
| ) | ||
| assert os.path.isfile(os.path.join(OUTPUTS_DIR, test_name, "summary.yaml")) | ||
|
|
||
| # Check file contains key symbols | ||
| with open(os.path.join(OUTPUTS_DIR, test_name, "summary.yaml")) as f: | ||
|
roman-janik-nxp marked this conversation as resolved.
|
||
| content = f.read() | ||
| keys = [ | ||
| "date_time", | ||
| "eiq_neutron_sdk_version", | ||
| "eiq_nsys_version", | ||
| "git_branch", | ||
| "git_commit", | ||
| "test_name", | ||
| ] | ||
| assert all(key in content for key in keys) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "tag1_neutron.et.tflite") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "tag1_pure.et.tflite") | ||
| ) | ||
|
|
||
| # Check text tensor variants | ||
| assert os.path.isfile( | ||
| os.path.join( | ||
| OUTPUTS_DIR, test_name, "dataset", "calibration", "0000", "00.txt" | ||
| ) | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "results_cpu", "0000", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join(OUTPUTS_DIR, test_name, "results_npu", "0000", "0000.txt") | ||
| ) | ||
| assert os.path.isfile( | ||
| os.path.join( | ||
| OUTPUTS_DIR, test_name, "diff_cpu_npu_results", "0000", "0000.txt" | ||
| ) | ||
| ) | ||
| assert os.path.isfile(os.path.join(OUTPUTS_DIR, f"{test_name}.zip")) | ||
Uh oh!
There was an error while loading. Please reload this page.