tensoracle 1.5.0__tar.gz → 1.6.0__tar.gz
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.
- {tensoracle-1.5.0 → tensoracle-1.6.0}/PKG-INFO +8 -4
- {tensoracle-1.5.0 → tensoracle-1.6.0}/README.md +7 -3
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/index.md +4 -2
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_build.py +11 -6
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_cache.py +10 -3
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_cli.py +1 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_version.py +2 -2
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_build.py +31 -6
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cache.py +2 -2
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cache_cli.py +26 -2
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cache_management.py +43 -2
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_weight_hash.py +2 -1
- {tensoracle-1.5.0 → tensoracle-1.6.0}/.gitignore +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/LICENSE +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/build_backend.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/CMakeLists.txt +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/cmake/TensoracleCubinPlugin.cmake +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/cmake/TensoracleCuda.cmake +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/include/tensoracle/cubin_plugin.hpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/src/cubin_plugin.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/cpp/tests/core.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/api/bundle.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/api/index.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/api/producer.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/cpp-python-repositories.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/optimization.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/precision.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/docs/tensorrt-sdk.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/CMakeLists.txt +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/README.md +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/bundle.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/bundle.json +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm120/bias.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm120/dot.specialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm120/dot.tf32-control.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm120/dot.unspecialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm120/scale.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/bias.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/dot.specialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/dot.tf32-control.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/dot.unspecialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/scale.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/bias.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/dot.specialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/dot.tf32-control.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/dot.unspecialized.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/scale.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/example_cubin_plugin/__init__.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/example_cubin_plugin/weights.safetensors +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/kernels.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/plugin.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/pyproject.toml +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/mkdocs.yml +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/pyproject.toml +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/__init__.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_cache_format.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_comparison.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_execution.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_memory.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/_plugins.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/bundle.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/convert.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/src/tensoracle/producer.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/build_memory_runtime.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubin_io/check.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubin_io/kernels.cu +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubin_io/plugin.cpp +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubins/ptxas-12.9.86/sm120.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubins/ptxas-12.9.86/sm86.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/cubins/ptxas-12.9.86/sm89.cubin +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/example_runtime.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/native/CMakeLists.txt +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/native/export.h +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/native_build.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/plugin_io_runtime.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/precision_runtime.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_build_memory.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_bundle.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cmake_cuda.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cmake_plugin.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_comparison.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_convert.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cpp_core.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_cuda_graphs.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_distribution.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_engine_io.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_example_plugin.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_execution.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_gpu_cache.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_plugin_io.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_plugins.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_precision.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_producer.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/tests/test_shape_tensor_outputs.py +0 -0
- {tensoracle-1.5.0 → tensoracle-1.6.0}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tensoracle
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.6.0
|
|
4
4
|
Summary: Build, save, reuse, and execute TensorRT engines from safetensors weights
|
|
5
5
|
Project-URL: documentation, https://sync-dev-org.github.io/tensoracle/
|
|
6
6
|
Project-URL: repository, https://github.com/sync-dev-org/tensoracle
|
|
@@ -110,7 +110,7 @@ def graph(network: trt.INetworkDefinition) -> None:
|
|
|
110
110
|
|
|
111
111
|
declaration = EngineBuild(
|
|
112
112
|
inputs={"features": InputShape.ranged((1, 1, 2), (1, 64, 2), (1, 448, 2))},
|
|
113
|
-
settings=BuilderSettings(workspace_limit=
|
|
113
|
+
settings=BuilderSettings(workspace_limit=None, optimization_level=3),
|
|
114
114
|
)
|
|
115
115
|
plan: bytes = build_serialized_engine(graph, declaration)
|
|
116
116
|
```
|
|
@@ -119,6 +119,8 @@ Use `InputShape.fixed((1, 64, 2))` for one input size; the graph may declare tha
|
|
|
119
119
|
|
|
120
120
|
The engine's precision is determined by the tensor types that the graph function places in the network. For an fp16 graph, use `trt.float16` tensors and `np.float16` weights where needed; the graph can also contain fp32 tensors. `EngineBuild` declares input shapes and builder settings. TF32 is always disabled; `BuilderSettings(tf32=True)` raises `ValueError`. The function returns serialized plan bytes. Invalid declarations raise `ValueError`, and a failed TensorRT build raises `RuntimeError`.
|
|
121
121
|
|
|
122
|
+
`BuilderSettings.workspace_limit` defaults to `None`, which leaves the workspace limit at TensorRT's default without calling `set_memory_pool_limit`. To impose a limit, pass a positive integer in bytes, such as `workspace_limit=4 << 30` for 4 GiB. Zero, negative values, booleans, and noninteger values raise `ValueError`.
|
|
123
|
+
|
|
122
124
|
The same API builds bf16, int8, and fp8 graphs. Cast tensors to `trt.bfloat16` for bf16; for int8/fp8, place `add_quantize` / `add_dequantize` layers and load their scales from safetensors. The application determines scales with an external quantization library and chooses output quality limits with `compare_outputs`. fp8 targets sm89 and sm120; bf16 and int8 also target sm86. Follow [Build bf16, int8, and fp8 engines](https://github.com/sync-dev-org/tensoracle/blob/main/docs/precision.md) for a complete example, NumPy dtype constraints, and comparison against an fp32 engine. Use different engine names or graph versions for different precisions.
|
|
123
125
|
|
|
124
126
|
## Save and reopen an engine
|
|
@@ -144,7 +146,9 @@ handle = open_engine(
|
|
|
144
146
|
engine = handle.engine # Keep handle.runtime alive while using the engine.
|
|
145
147
|
```
|
|
146
148
|
|
|
147
|
-
Saved files live in the `tensoracle` directory under `XDG_CACHE_HOME`, or under the home directory's `.cache` when that variable is unset. The same rule applies on every OS; on Windows the home directory is `%USERPROFILE%`, so the default is `%USERPROFILE%\.cache\tensoracle`. The cache identity includes the contents of all declared safetensors weight files and optional `plugins` files, the engine name, graph version, input shapes, builder settings, the GPU's compatibility properties, OS, CPU architecture, and TensorRT version. The GPU is the device of the current CUDA context, or device 0 when no context is current. Its properties are the product name, compute capability, memory bus width, L2 cache size, shared memory limits, texture alignment, multiprocessor count, whether the GPU is integrated, ECC mode, and total memory; another device with the same properties reuses the saved engine. Raise `graph_version` when graph code changes; without a version change, the previous engine is reused. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. Include in `graph_version` any other caller value that changes the engine contents but is not part of the cache identity, such as a plugin manifest or a build recipe. Pass `force_rebuild=True` to replace a cached engine even when it is intact. tensoracle closes a saved engine file after reading it, so another process using an opened engine does not block replacement. On Windows, a process that keeps the saved file itself open prevents replacement or removal with `PermissionError`; after that process closes the file, a file left empty by the failed rebuild is rejected as `CorruptEngineError` and can be rebuilt with `force_rebuild=True`.
|
|
149
|
+
Saved files live in the `tensoracle` directory under `XDG_CACHE_HOME`, or under the home directory's `.cache` when that variable is unset. The same rule applies on every OS; on Windows the home directory is `%USERPROFILE%`, so the default is `%USERPROFILE%\.cache\tensoracle`. The cache identity includes the contents of all declared safetensors weight files and optional `plugins` files, the engine name, graph version, input shapes, the builder's `optimization_level` and `tf32` settings, the GPU's compatibility properties, OS, CPU architecture, and TensorRT version. The GPU is the device of the current CUDA context, or device 0 when no context is current. Its properties are the product name, compute capability, memory bus width, L2 cache size, shared memory limits, texture alignment, multiprocessor count, whether the GPU is integrated, ECC mode, and total memory; another device with the same properties reuses the saved engine. Raise `graph_version` when graph code changes; without a version change, the previous engine is reused. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. Include in `graph_version` any other caller value that changes the engine contents but is not part of the cache identity, such as a plugin manifest or a build recipe. Pass `force_rebuild=True` to replace a cached engine even when it is intact. tensoracle closes a saved engine file after reading it, so another process using an opened engine does not block replacement. On Windows, a process that keeps the saved file itself open prevents replacement or removal with `PermissionError`; after that process closes the file, a file left empty by the failed rebuild is rejected as `CorruptEngineError` and can be rebuilt with `force_rebuild=True`.
|
|
150
|
+
|
|
151
|
+
`workspace_limit` is excluded from the cache identity. Changing it reuses a matching saved engine; pass `force_rebuild=True` to `open_engine` to build again with the new limit. The saved header records the workspace limit used for that build, including `None`, as a reference outside the conditions used for matching. Cache reads leave this reference unchanged. `tensoracle cache list` shows the recorded limit.
|
|
148
152
|
|
|
149
153
|
`plugins` names native TensorRT plugin libraries. Both `open_engine` and `build_serialized_engine` accept it. Each function checks that every library exports `getCreators`, loads it through TensorRT's registry, and retains it for the process before building the graph or deserializing an engine. Repeating the same library does not register it twice. `open_engine` also hashes the loaded library bytes into the engine identity; a changed library selects a different saved engine in a new process. A missing, unreadable, or incompatible library, or a library changed after loading in the same process, raises `PluginLibraryError` before a cached plan is opened. Keep the library and its cubin bundle together in the caller's wheel.
|
|
150
154
|
|
|
@@ -170,7 +174,7 @@ tensoracle cache prune --max-size 10GiB
|
|
|
170
174
|
|
|
171
175
|
The commands use the same `XDG_CACHE_HOME` / home `.cache` directory as `open_engine`. They read only filesystem metadata, engine headers, and saved weight digest records. They do not read or checksum the plan body, load the original weights, deserialize engines, import TensorRT, or query a GPU. Listing recognizes the header format; it does not establish that an engine can be loaded.
|
|
172
176
|
|
|
173
|
-
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
177
|
+
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. Each recognized entry also contains `workspace_limit`: a positive integer in bytes for an explicit build limit, JSON `null` when the build left the limit at TensorRT's default, or `"unknown"` when the header has no reference value. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
174
178
|
|
|
175
179
|
An engine's `.engine` modification time records its initial save or the last successful `open_engine` cache read. Failed loads and these management commands leave that time unchanged. A timestamp write failure, including a read-only cache, does not prevent a successful load, and a disappeared file is never recreated to record its time. The record describes the time of `open_engine`, not whether inference continues afterward. Files saved before timestamp recording was introduced, and files whose timestamp could not be written, may not reflect the actual last successful read.
|
|
176
180
|
|
|
@@ -77,7 +77,7 @@ def graph(network: trt.INetworkDefinition) -> None:
|
|
|
77
77
|
|
|
78
78
|
declaration = EngineBuild(
|
|
79
79
|
inputs={"features": InputShape.ranged((1, 1, 2), (1, 64, 2), (1, 448, 2))},
|
|
80
|
-
settings=BuilderSettings(workspace_limit=
|
|
80
|
+
settings=BuilderSettings(workspace_limit=None, optimization_level=3),
|
|
81
81
|
)
|
|
82
82
|
plan: bytes = build_serialized_engine(graph, declaration)
|
|
83
83
|
```
|
|
@@ -86,6 +86,8 @@ Use `InputShape.fixed((1, 64, 2))` for one input size; the graph may declare tha
|
|
|
86
86
|
|
|
87
87
|
The engine's precision is determined by the tensor types that the graph function places in the network. For an fp16 graph, use `trt.float16` tensors and `np.float16` weights where needed; the graph can also contain fp32 tensors. `EngineBuild` declares input shapes and builder settings. TF32 is always disabled; `BuilderSettings(tf32=True)` raises `ValueError`. The function returns serialized plan bytes. Invalid declarations raise `ValueError`, and a failed TensorRT build raises `RuntimeError`.
|
|
88
88
|
|
|
89
|
+
`BuilderSettings.workspace_limit` defaults to `None`, which leaves the workspace limit at TensorRT's default without calling `set_memory_pool_limit`. To impose a limit, pass a positive integer in bytes, such as `workspace_limit=4 << 30` for 4 GiB. Zero, negative values, booleans, and noninteger values raise `ValueError`.
|
|
90
|
+
|
|
89
91
|
The same API builds bf16, int8, and fp8 graphs. Cast tensors to `trt.bfloat16` for bf16; for int8/fp8, place `add_quantize` / `add_dequantize` layers and load their scales from safetensors. The application determines scales with an external quantization library and chooses output quality limits with `compare_outputs`. fp8 targets sm89 and sm120; bf16 and int8 also target sm86. Follow [Build bf16, int8, and fp8 engines](https://github.com/sync-dev-org/tensoracle/blob/main/docs/precision.md) for a complete example, NumPy dtype constraints, and comparison against an fp32 engine. Use different engine names or graph versions for different precisions.
|
|
90
92
|
|
|
91
93
|
## Save and reopen an engine
|
|
@@ -111,7 +113,9 @@ handle = open_engine(
|
|
|
111
113
|
engine = handle.engine # Keep handle.runtime alive while using the engine.
|
|
112
114
|
```
|
|
113
115
|
|
|
114
|
-
Saved files live in the `tensoracle` directory under `XDG_CACHE_HOME`, or under the home directory's `.cache` when that variable is unset. The same rule applies on every OS; on Windows the home directory is `%USERPROFILE%`, so the default is `%USERPROFILE%\.cache\tensoracle`. The cache identity includes the contents of all declared safetensors weight files and optional `plugins` files, the engine name, graph version, input shapes, builder settings, the GPU's compatibility properties, OS, CPU architecture, and TensorRT version. The GPU is the device of the current CUDA context, or device 0 when no context is current. Its properties are the product name, compute capability, memory bus width, L2 cache size, shared memory limits, texture alignment, multiprocessor count, whether the GPU is integrated, ECC mode, and total memory; another device with the same properties reuses the saved engine. Raise `graph_version` when graph code changes; without a version change, the previous engine is reused. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. Include in `graph_version` any other caller value that changes the engine contents but is not part of the cache identity, such as a plugin manifest or a build recipe. Pass `force_rebuild=True` to replace a cached engine even when it is intact. tensoracle closes a saved engine file after reading it, so another process using an opened engine does not block replacement. On Windows, a process that keeps the saved file itself open prevents replacement or removal with `PermissionError`; after that process closes the file, a file left empty by the failed rebuild is rejected as `CorruptEngineError` and can be rebuilt with `force_rebuild=True`.
|
|
116
|
+
Saved files live in the `tensoracle` directory under `XDG_CACHE_HOME`, or under the home directory's `.cache` when that variable is unset. The same rule applies on every OS; on Windows the home directory is `%USERPROFILE%`, so the default is `%USERPROFILE%\.cache\tensoracle`. The cache identity includes the contents of all declared safetensors weight files and optional `plugins` files, the engine name, graph version, input shapes, the builder's `optimization_level` and `tf32` settings, the GPU's compatibility properties, OS, CPU architecture, and TensorRT version. The GPU is the device of the current CUDA context, or device 0 when no context is current. Its properties are the product name, compute capability, memory bus width, L2 cache size, shared memory limits, texture alignment, multiprocessor count, whether the GPU is integrated, ECC mode, and total memory; another device with the same properties reuses the saved engine. Raise `graph_version` when graph code changes; without a version change, the previous engine is reused. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. Include in `graph_version` any other caller value that changes the engine contents but is not part of the cache identity, such as a plugin manifest or a build recipe. Pass `force_rebuild=True` to replace a cached engine even when it is intact. tensoracle closes a saved engine file after reading it, so another process using an opened engine does not block replacement. On Windows, a process that keeps the saved file itself open prevents replacement or removal with `PermissionError`; after that process closes the file, a file left empty by the failed rebuild is rejected as `CorruptEngineError` and can be rebuilt with `force_rebuild=True`.
|
|
117
|
+
|
|
118
|
+
`workspace_limit` is excluded from the cache identity. Changing it reuses a matching saved engine; pass `force_rebuild=True` to `open_engine` to build again with the new limit. The saved header records the workspace limit used for that build, including `None`, as a reference outside the conditions used for matching. Cache reads leave this reference unchanged. `tensoracle cache list` shows the recorded limit.
|
|
115
119
|
|
|
116
120
|
`plugins` names native TensorRT plugin libraries. Both `open_engine` and `build_serialized_engine` accept it. Each function checks that every library exports `getCreators`, loads it through TensorRT's registry, and retains it for the process before building the graph or deserializing an engine. Repeating the same library does not register it twice. `open_engine` also hashes the loaded library bytes into the engine identity; a changed library selects a different saved engine in a new process. A missing, unreadable, or incompatible library, or a library changed after loading in the same process, raises `PluginLibraryError` before a cached plan is opened. Keep the library and its cubin bundle together in the caller's wheel.
|
|
117
121
|
|
|
@@ -137,7 +141,7 @@ tensoracle cache prune --max-size 10GiB
|
|
|
137
141
|
|
|
138
142
|
The commands use the same `XDG_CACHE_HOME` / home `.cache` directory as `open_engine`. They read only filesystem metadata, engine headers, and saved weight digest records. They do not read or checksum the plan body, load the original weights, deserialize engines, import TensorRT, or query a GPU. Listing recognizes the header format; it does not establish that an engine can be loaded.
|
|
139
143
|
|
|
140
|
-
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
144
|
+
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. Each recognized entry also contains `workspace_limit`: a positive integer in bytes for an explicit build limit, JSON `null` when the build left the limit at TensorRT's default, or `"unknown"` when the header has no reference value. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
141
145
|
|
|
142
146
|
An engine's `.engine` modification time records its initial save or the last successful `open_engine` cache read. Failed loads and these management commands leave that time unchanged. A timestamp write failure, including a read-only cache, does not prevent a successful load, and a disappeared file is never recreated to record its time. The record describes the time of `open_engine`, not whether inference continues afterward. Files saved before timestamp recording was introduced, and files whose timestamp could not be written, may not reflect the actual last successful read.
|
|
143
147
|
|
|
@@ -74,6 +74,8 @@ handle = open_engine(
|
|
|
74
74
|
|
|
75
75
|
`my_model` represents the application's model package. On a valid cache hit, `open_engine` does not call `graph`; on a miss, it builds and saves a new engine. Raise `graph_version` when the graph code changes or when any other caller value that changes the engine contents but is not part of the cache identity changes, such as a plugin manifest or a build recipe. `open_engine` itself does not execute the engine or manage GPU buffers. Create an execution unit from its returned handle to enqueue inference. Saved engines live under `XDG_CACHE_HOME/tensoracle`, or `~/.cache/tensoracle` when `XDG_CACHE_HOME` is unset. On Windows, `~` is `%USERPROFILE%`. See the [complete cache example](https://github.com/sync-dev-org/tensoracle#save-and-reopen-an-engine) for the declaration and expected input and output types.
|
|
76
76
|
|
|
77
|
+
`workspace_limit` is excluded from the cache identity; `optimization_level` and `tf32` remain in the conditions. Changing only the workspace limit reuses a matching saved engine. Pass `force_rebuild=True` to `open_engine` to build again with the new limit. The saved header records the workspace limit used for that build, including `None`, as a reference outside the conditions used for matching. Cache reads leave this reference unchanged. `tensoracle cache list` shows the recorded limit.
|
|
78
|
+
|
|
77
79
|
### Manage saved engines
|
|
78
80
|
|
|
79
81
|
Inspect saved engines and preview or perform cleanup with the installed command:
|
|
@@ -88,7 +90,7 @@ tensoracle cache prune --max-size 10GiB
|
|
|
88
90
|
|
|
89
91
|
The commands use the same `XDG_CACHE_HOME` / home `.cache` directory as `open_engine`. They read only filesystem metadata, engine headers, and saved weight digest records. They do not read or checksum the plan body, load the original weights, deserialize engines, import TensorRT, or query a GPU. Listing recognizes the header format; it does not establish that an engine can be loaded.
|
|
90
92
|
|
|
91
|
-
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
93
|
+
`cache list` prints one JSON object with `directory`, `entries`, and `total_size_bytes`. Each entry contains the file name, size in bytes, recorded time in UTC (`recorded_at`) and exact nanoseconds (`recorded_at_ns`), and the complete header conditions. This distinguishes engines with the same name and graph version but different weights or plugins. Each recognized entry also contains `workspace_limit`: a positive integer in bytes for an explicit build limit, JSON `null` when the build left the limit at TensorRT's default, or `"unknown"` when the header has no reference value. `weight_paths` associates each weight digest with paths from valid, checksum-verified `weight-*.json` records whose SHA-256 matches that digest; an empty `paths` array means unknown. These paths are references and do not establish which application uses an engine or whether the weight file still exists. Empty files have status `building or leftover`; unreadable or invalid headers have status `unrecognized` and a reason.
|
|
92
94
|
|
|
93
95
|
An engine's `.engine` modification time records its initial save or the last successful `open_engine` cache read. Failed loads and these management commands leave that time unchanged. A timestamp write failure, including a read-only cache, does not prevent a successful load, and a disappeared file is never recreated to record its time. The record describes the time of `open_engine`, not whether inference continues afterward. Files saved before timestamp recording was introduced, and files whose timestamp could not be written, may not reflect the actual last successful read.
|
|
94
96
|
|
|
@@ -252,7 +254,7 @@ Engines with loops or conditionals, or layers or plugins that synchronize with t
|
|
|
252
254
|
|
|
253
255
|
### Build a standard graph
|
|
254
256
|
|
|
255
|
-
Define `graph(network)` with TensorRT's Python network API and load model weights from safetensors. Describe each graph input with `InputShape.fixed` or `InputShape.ranged`, and pass an `EngineBuild` declaration to `build_serialized_engine`. `EngineBuild` declares input shapes and builder settings; the graph function determines precision through the tensor types it places in the network. The supported precisions are fp32 (with TF32 disabled), fp16, bf16, int8, and fp8. The [bf16, int8, and fp8 guide](precision.md) shows tensor casts, explicit Q/DQ layers, and scales supplied through safetensors; fp8 targets sm89 and sm120. `open_engine` uses the same declaration plus a name, graph version, weight files, and `EngineIO` to save and reuse a checked engine. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. The [graph example](https://github.com/sync-dev-org/tensoracle#define-and-build-a-standard-graph) shows the exact calls.
|
|
257
|
+
Define `graph(network)` with TensorRT's Python network API and load model weights from safetensors. Describe each graph input with `InputShape.fixed` or `InputShape.ranged`, and pass an `EngineBuild` declaration to `build_serialized_engine`. `EngineBuild` declares input shapes and builder settings; the graph function determines precision through the tensor types it places in the network. `BuilderSettings.workspace_limit` defaults to `None`, leaving the workspace limit at TensorRT's default. To impose a limit, pass a positive integer in bytes, such as `workspace_limit=4 << 30` for 4 GiB. Zero, negative values, booleans, and noninteger values raise `ValueError`. The supported precisions are fp32 (with TF32 disabled), fp16, bf16, int8, and fp8. The [bf16, int8, and fp8 guide](precision.md) shows tensor casts, explicit Q/DQ layers, and scales supplied through safetensors; fp8 targets sm89 and sm120. `open_engine` uses the same declaration plus a name, graph version, weight files, and `EngineIO` to save and reuse a checked engine. Use a different `name` or `graph_version` for engines with different tensor types, such as fp32 and fp16. The [graph example](https://github.com/sync-dev-org/tensoracle#define-and-build-a-standard-graph) shows the exact calls.
|
|
256
258
|
|
|
257
259
|
The application runs the engine and passes a CPU NumPy candidate output and a corresponding reference output to `compare_outputs`. The application chooses the acceptance limits. See [Compare output arrays](https://github.com/sync-dev-org/tensoracle#compare-output-arrays).
|
|
258
260
|
|
|
@@ -69,10 +69,16 @@ class InputShape:
|
|
|
69
69
|
class BuilderSettings:
|
|
70
70
|
"""TensorRT builder options that affect the serialized engine."""
|
|
71
71
|
|
|
72
|
-
workspace_limit: int
|
|
72
|
+
workspace_limit: int | None = None
|
|
73
73
|
optimization_level: int = 3
|
|
74
74
|
tf32: bool = False
|
|
75
75
|
|
|
76
|
+
def __post_init__(self) -> None:
|
|
77
|
+
if self.workspace_limit is not None and (
|
|
78
|
+
type(self.workspace_limit) is not int or self.workspace_limit <= 0
|
|
79
|
+
):
|
|
80
|
+
raise ValueError("workspace_limit must be a positive integer or None")
|
|
81
|
+
|
|
76
82
|
|
|
77
83
|
@dataclass(frozen=True)
|
|
78
84
|
class EngineBuild:
|
|
@@ -103,8 +109,6 @@ def _build_serialized_engine(
|
|
|
103
109
|
) -> tuple[bytes, tuple[str, ...]]:
|
|
104
110
|
if declaration.settings.tf32:
|
|
105
111
|
raise ValueError("TF32 cannot be enabled")
|
|
106
|
-
if declaration.settings.workspace_limit <= 0:
|
|
107
|
-
raise ValueError("workspace_limit must be positive")
|
|
108
112
|
if not 0 <= declaration.settings.optimization_level <= 5:
|
|
109
113
|
raise ValueError("optimization_level must be between 0 and 5")
|
|
110
114
|
|
|
@@ -125,9 +129,10 @@ def _build_serialized_engine(
|
|
|
125
129
|
_warn_shape_tensor_outputs(output_names, stacklevel=3)
|
|
126
130
|
config = builder.create_builder_config()
|
|
127
131
|
config.clear_flag(trt.BuilderFlag.TF32)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
132
|
+
if declaration.settings.workspace_limit is not None:
|
|
133
|
+
config.set_memory_pool_limit(
|
|
134
|
+
trt.MemoryPoolType.WORKSPACE, declaration.settings.workspace_limit
|
|
135
|
+
)
|
|
131
136
|
config.builder_optimization_level = declaration.settings.optimization_level
|
|
132
137
|
|
|
133
138
|
inputs = {
|
|
@@ -7,7 +7,7 @@ import os
|
|
|
7
7
|
import platform
|
|
8
8
|
import tempfile
|
|
9
9
|
from collections.abc import Callable, Mapping, Sequence
|
|
10
|
-
from dataclasses import
|
|
10
|
+
from dataclasses import dataclass
|
|
11
11
|
from dataclasses import field as dataclass_field
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import TYPE_CHECKING
|
|
@@ -125,7 +125,11 @@ def open_engine(
|
|
|
125
125
|
handle = _open_plan(
|
|
126
126
|
plan, path, declaration, io, actual_logger, shape_tensor_outputs=shape_tensor_outputs
|
|
127
127
|
)
|
|
128
|
-
header = {
|
|
128
|
+
header = {
|
|
129
|
+
"conditions": conditions,
|
|
130
|
+
"plan_sha256": hashlib.sha256(plan).hexdigest(),
|
|
131
|
+
"workspace_limit": declaration.settings.workspace_limit,
|
|
132
|
+
}
|
|
129
133
|
if shape_tensor_outputs:
|
|
130
134
|
header["shape_tensor_outputs"] = list(shape_tensor_outputs)
|
|
131
135
|
header_bytes = _json_bytes(header)
|
|
@@ -286,7 +290,10 @@ def _conditions(
|
|
|
286
290
|
}
|
|
287
291
|
for name, shape in declaration.inputs.items()
|
|
288
292
|
},
|
|
289
|
-
"builder_settings":
|
|
293
|
+
"builder_settings": {
|
|
294
|
+
"optimization_level": declaration.settings.optimization_level,
|
|
295
|
+
"tf32": declaration.settings.tf32,
|
|
296
|
+
},
|
|
290
297
|
"gpu": _gpu_identity(),
|
|
291
298
|
"os": platform.system(),
|
|
292
299
|
"architecture": platform.machine(),
|
|
@@ -58,6 +58,7 @@ def _inspect(path: Path) -> _Entry:
|
|
|
58
58
|
raise OSError("file changed during inspection")
|
|
59
59
|
details["status"] = "recognized"
|
|
60
60
|
details["conditions"] = header["conditions"]
|
|
61
|
+
details["workspace_limit"] = header.get("workspace_limit", "unknown")
|
|
61
62
|
except (OSError, CorruptEngineError) as error:
|
|
62
63
|
details["reason"] = str(error)
|
|
63
64
|
return _Entry(path, observed, details)
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '1.
|
|
22
|
-
__version_tuple__ = version_tuple = (1,
|
|
21
|
+
__version__ = version = '1.6.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (1, 6, 0)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -187,8 +187,23 @@ def test_tf32_request_is_rejected_before_graph_is_built():
|
|
|
187
187
|
|
|
188
188
|
|
|
189
189
|
@pytest.mark.req("SP-TENSORACLE-013")
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
191
|
+
@pytest.mark.parametrize(
|
|
192
|
+
"settings, workspace_calls",
|
|
193
|
+
[
|
|
194
|
+
(BuilderSettings(optimization_level=2), []),
|
|
195
|
+
(BuilderSettings(workspace_limit=None, optimization_level=2), []),
|
|
196
|
+
(
|
|
197
|
+
BuilderSettings(workspace_limit=4096, optimization_level=2),
|
|
198
|
+
[(trt.MemoryPoolType.WORKSPACE, 4096)],
|
|
199
|
+
),
|
|
200
|
+
],
|
|
201
|
+
ids=["default-workspace", "explicit-none", "positive-workspace"],
|
|
202
|
+
)
|
|
203
|
+
def test_tf32_is_disabled_and_other_builder_settings_are_applied(
|
|
204
|
+
monkeypatch, settings, workspace_calls
|
|
205
|
+
):
|
|
206
|
+
"""The builder clears TF32 and sets a workspace limit only for an explicit positive integer."""
|
|
192
207
|
import tensoracle._build as module
|
|
193
208
|
|
|
194
209
|
class Config:
|
|
@@ -196,13 +211,13 @@ def test_tf32_is_disabled_and_other_builder_settings_are_applied(monkeypatch):
|
|
|
196
211
|
|
|
197
212
|
def __init__(self):
|
|
198
213
|
self.flags = {trt.BuilderFlag.TF32}
|
|
199
|
-
self.
|
|
214
|
+
self.workspace_calls = []
|
|
200
215
|
|
|
201
216
|
def clear_flag(self, flag):
|
|
202
217
|
self.flags.discard(flag)
|
|
203
218
|
|
|
204
219
|
def set_memory_pool_limit(self, pool, limit):
|
|
205
|
-
self.
|
|
220
|
+
self.workspace_calls.append((pool, limit))
|
|
206
221
|
|
|
207
222
|
class Network:
|
|
208
223
|
num_inputs = 1
|
|
@@ -229,10 +244,20 @@ def test_tf32_is_disabled_and_other_builder_settings_are_applied(monkeypatch):
|
|
|
229
244
|
monkeypatch.setattr(module.trt, "Builder", Builder)
|
|
230
245
|
declaration = EngineBuild(
|
|
231
246
|
inputs={"features": InputShape.fixed((1, 3, 2))},
|
|
232
|
-
settings=
|
|
247
|
+
settings=settings,
|
|
233
248
|
)
|
|
234
249
|
assert build_serialized_engine(lambda network: None, declaration) == b"plan"
|
|
235
250
|
config = observed[0].config
|
|
236
251
|
assert trt.BuilderFlag.TF32 not in config.flags
|
|
237
|
-
assert config.
|
|
252
|
+
assert config.workspace_calls == workspace_calls
|
|
238
253
|
assert config.builder_optimization_level == 2
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
257
|
+
@pytest.mark.parametrize("workspace_limit", [0, -1, True, False, 1.5, "4096"])
|
|
258
|
+
def test_invalid_workspace_limit_is_rejected_before_building(monkeypatch, workspace_limit):
|
|
259
|
+
"""Zero, negatives, booleans, and nonintegers fail before creating a TensorRT builder."""
|
|
260
|
+
monkeypatch.setattr(trt, "Builder", lambda logger: pytest.fail("builder was created"))
|
|
261
|
+
with pytest.raises(ValueError, match="workspace_limit"):
|
|
262
|
+
declaration = EngineBuild({}, BuilderSettings(workspace_limit=workspace_limit))
|
|
263
|
+
build_serialized_engine(lambda network: pytest.fail("graph was built"), declaration)
|
|
@@ -347,6 +347,7 @@ def test_corrupt_plan_and_changed_header_fail_before_deserialization(tmp_path, m
|
|
|
347
347
|
@pytest.mark.gpu
|
|
348
348
|
@pytest.mark.req("SP-TENSORACLE-002")
|
|
349
349
|
@pytest.mark.req("SP-TENSORACLE-011")
|
|
350
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
350
351
|
def test_force_rebuild_uses_changed_weight_contents_and_preserves_old_engine(tmp_path, monkeypatch):
|
|
351
352
|
"""A forced rebuild saves under the new digest without replacing the old engine."""
|
|
352
353
|
monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))
|
|
@@ -378,7 +379,7 @@ def test_force_rebuild_uses_changed_weight_contents_and_preserves_old_engine(tmp
|
|
|
378
379
|
offset = len(b"TENSORACLE\x01")
|
|
379
380
|
old_header_size = int.from_bytes(old_engine[offset : offset + 8], "big")
|
|
380
381
|
old_header = json.loads(old_engine[offset + 8 : offset + 8 + old_header_size])
|
|
381
|
-
assert old_header.keys() == {"conditions", "plan_sha256"}
|
|
382
|
+
assert old_header.keys() == {"conditions", "plan_sha256", "workspace_limit"}
|
|
382
383
|
|
|
383
384
|
before = weights.stat()
|
|
384
385
|
changed = bytearray(weights.read_bytes())
|
|
@@ -618,7 +619,6 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
|
|
|
618
619
|
{"shape": (1, 4, 2)},
|
|
619
620
|
{"input_range": ((1, 1, 2), (1, 3, 2), (1, 5, 2))},
|
|
620
621
|
{"input_range": ((1, 1, 2), (1, 3, 2), (1, 6, 2))},
|
|
621
|
-
{"settings": tensoracle.BuilderSettings(workspace_limit=2048, optimization_level=0)},
|
|
622
622
|
{"settings": tensoracle.BuilderSettings(optimization_level=1)},
|
|
623
623
|
]
|
|
624
624
|
paths = {open_case(**variant).path for variant in variants}
|
|
@@ -22,7 +22,9 @@ def saved_cache(tmp_path, monkeypatch):
|
|
|
22
22
|
return directory
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def save_engine(
|
|
25
|
+
def save_engine(
|
|
26
|
+
directory, filename="one.engine", *, conditions=None, mtime=OLD_NS, size=None, metadata=None
|
|
27
|
+
):
|
|
26
28
|
if conditions is None:
|
|
27
29
|
conditions = {
|
|
28
30
|
"name": "encoder",
|
|
@@ -36,7 +38,9 @@ def save_engine(directory, filename="one.engine", *, conditions=None, mtime=OLD_
|
|
|
36
38
|
"architecture": "x86_64",
|
|
37
39
|
"tensorrt_version": "11.0",
|
|
38
40
|
}
|
|
39
|
-
header = json.dumps(
|
|
41
|
+
header = json.dumps(
|
|
42
|
+
{"conditions": conditions, "plan_sha256": "0" * 64, **(metadata or {})}
|
|
43
|
+
).encode()
|
|
40
44
|
path = directory / filename
|
|
41
45
|
path.write_bytes(b"TENSORACLE\x01" + len(header).to_bytes(8, "big") + header + b"plan")
|
|
42
46
|
if size is not None:
|
|
@@ -104,6 +108,26 @@ def test_list_distinguishes_all_conditions_and_preserves_recorded_time(saved_cac
|
|
|
104
108
|
)
|
|
105
109
|
|
|
106
110
|
|
|
111
|
+
@pytest.mark.req("SP-TENSORACLE-043")
|
|
112
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
113
|
+
@pytest.mark.parametrize(
|
|
114
|
+
"metadata, expected",
|
|
115
|
+
[({"workspace_limit": None}, None), ({"workspace_limit": 4096}, 4096), ({}, "unknown")],
|
|
116
|
+
ids=["tensorrt-default", "explicit-limit", "missing-record"],
|
|
117
|
+
)
|
|
118
|
+
def test_list_shows_recorded_workspace_limit_or_unknown(saved_cache, capsys, metadata, expected):
|
|
119
|
+
"""Listing distinguishes TensorRT's default, an explicit byte limit, and a missing record."""
|
|
120
|
+
path, conditions = save_engine(saved_cache, metadata=metadata)
|
|
121
|
+
contents = path.read_bytes()
|
|
122
|
+
status, (listing,), stderr = call_cli(capsys, "list")
|
|
123
|
+
assert status == 0 and not stderr
|
|
124
|
+
entry = listing["entries"][0]
|
|
125
|
+
assert entry["workspace_limit"] == expected
|
|
126
|
+
assert entry["conditions"] == conditions
|
|
127
|
+
assert path.read_bytes() == contents
|
|
128
|
+
assert path.stat().st_mtime_ns == OLD_NS
|
|
129
|
+
|
|
130
|
+
|
|
107
131
|
@pytest.mark.req("SP-TENSORACLE-049")
|
|
108
132
|
@pytest.mark.parametrize(
|
|
109
133
|
"damage", ["missing", "json", "version", "type", "checksum", "digest", "mismatch", "valid"]
|
|
@@ -39,19 +39,60 @@ def open_cached(tmp_path, monkeypatch):
|
|
|
39
39
|
def graph(network):
|
|
40
40
|
built.append(True)
|
|
41
41
|
|
|
42
|
-
def open_engine(*, io=None):
|
|
42
|
+
def open_engine(*, io=None, settings=None, force_rebuild=False):
|
|
43
43
|
return tensoracle.open_engine(
|
|
44
44
|
graph,
|
|
45
|
-
tensoracle.EngineBuild({}),
|
|
45
|
+
tensoracle.EngineBuild({}, settings or tensoracle.BuilderSettings()),
|
|
46
46
|
name="timestamp",
|
|
47
47
|
graph_version="1",
|
|
48
48
|
weights=(weight,),
|
|
49
49
|
io=io or tensoracle.EngineIO({}, {}),
|
|
50
|
+
force_rebuild=force_rebuild,
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
return open_engine, built
|
|
53
54
|
|
|
54
55
|
|
|
56
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
57
|
+
@pytest.mark.parametrize("workspace_limit", [None, 8192])
|
|
58
|
+
def test_workspace_limit_changes_reuse_saved_engine(open_cached, workspace_limit):
|
|
59
|
+
"""Changing only the workspace limit reuses the same file without invoking the graph."""
|
|
60
|
+
open_engine, built = open_cached
|
|
61
|
+
first = open_engine(settings=tensoracle.BuilderSettings(workspace_limit=4096))
|
|
62
|
+
contents = first.path.read_bytes()
|
|
63
|
+
reopened = open_engine(settings=tensoracle.BuilderSettings(workspace_limit=workspace_limit))
|
|
64
|
+
assert reopened.path == first.path
|
|
65
|
+
assert built == [True]
|
|
66
|
+
assert first.path.read_bytes() == contents
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
70
|
+
@pytest.mark.req("SP-TENSORACLE-011")
|
|
71
|
+
@pytest.mark.parametrize("workspace_limit", [None, 4096])
|
|
72
|
+
def test_saved_engine_records_workspace_limit_and_updates_it_only_on_rebuild(
|
|
73
|
+
open_cached, workspace_limit
|
|
74
|
+
):
|
|
75
|
+
"""Headers record the actual build's workspace outside conditions and forced rebuilds replace it."""
|
|
76
|
+
open_engine, built = open_cached
|
|
77
|
+
|
|
78
|
+
def header(path):
|
|
79
|
+
with path.open("rb") as stream:
|
|
80
|
+
return cache._read_header(stream, path, path.stat().st_size)
|
|
81
|
+
|
|
82
|
+
first = open_engine(settings=tensoracle.BuilderSettings(workspace_limit=workspace_limit))
|
|
83
|
+
original = header(first.path)
|
|
84
|
+
assert original["workspace_limit"] == workspace_limit
|
|
85
|
+
assert original["conditions"]["builder_settings"] == {"optimization_level": 3, "tf32": False}
|
|
86
|
+
changed_limit = 8192 if workspace_limit is None else None
|
|
87
|
+
rebuilt = open_engine(
|
|
88
|
+
settings=tensoracle.BuilderSettings(workspace_limit=changed_limit), force_rebuild=True
|
|
89
|
+
)
|
|
90
|
+
assert rebuilt.path == first.path
|
|
91
|
+
assert header(rebuilt.path)["workspace_limit"] == changed_limit
|
|
92
|
+
assert header(rebuilt.path)["conditions"] == original["conditions"]
|
|
93
|
+
assert built == [True, True]
|
|
94
|
+
|
|
95
|
+
|
|
55
96
|
@pytest.mark.req("SP-TENSORACLE-042")
|
|
56
97
|
@pytest.mark.req("SP-TENSORACLE-048")
|
|
57
98
|
@pytest.mark.req("SP-TENSORACLE-046")
|
|
@@ -297,6 +297,7 @@ def test_record_lives_in_user_cache_directory(tmp_path, monkeypatch, xdg):
|
|
|
297
297
|
|
|
298
298
|
@pytest.mark.req("SP-TENSORACLE-002")
|
|
299
299
|
@pytest.mark.req("SP-TENSORACLE-009")
|
|
300
|
+
@pytest.mark.req("SP-TENSORACLE-052")
|
|
300
301
|
def test_engine_identity_matches_the_full_hash_calculation(tmp_path, monkeypatch):
|
|
301
302
|
"""Stored digests produce the same conditions and engine name as full file hashing."""
|
|
302
303
|
root = tmp_path / "cache"
|
|
@@ -312,7 +313,7 @@ def test_engine_identity_matches_the_full_hash_calculation(tmp_path, monkeypatch
|
|
|
312
313
|
"weights": [hashlib.sha256(weight.read_bytes()).hexdigest()],
|
|
313
314
|
"plugins": [hashlib.sha256(plugin.read_bytes()).hexdigest()],
|
|
314
315
|
"inputs": {"features": {"minimum": [1, 3, 2], "optimum": [1, 3, 2], "maximum": [1, 3, 2]}},
|
|
315
|
-
"builder_settings": {"
|
|
316
|
+
"builder_settings": {"optimization_level": 3, "tf32": False},
|
|
316
317
|
"gpu": {"name": "GPU A"},
|
|
317
318
|
"os": cache.platform.system(),
|
|
318
319
|
"architecture": cache.platform.machine(),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm86/dot.specialized.cubin
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/bundle/cubins/sm89/dot.specialized.cubin
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/example_cubin_plugin/__init__.py
RENAMED
|
File without changes
|
{tensoracle-1.5.0 → tensoracle-1.6.0}/examples/cubin_plugin/example_cubin_plugin/weights.safetensors
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|