tensoracle 1.6.0__tar.gz → 1.6.1__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.
Files changed (95) hide show
  1. {tensoracle-1.6.0 → tensoracle-1.6.1}/PKG-INFO +1 -1
  2. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_build.py +15 -5
  3. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_version.py +2 -2
  4. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_build.py +6 -0
  5. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_build_memory.py +2 -0
  6. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cache.py +13 -6
  7. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cache_management.py +4 -1
  8. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_gpu_cache.py +71 -5
  9. {tensoracle-1.6.0 → tensoracle-1.6.1}/.gitignore +0 -0
  10. {tensoracle-1.6.0 → tensoracle-1.6.1}/LICENSE +0 -0
  11. {tensoracle-1.6.0 → tensoracle-1.6.1}/README.md +0 -0
  12. {tensoracle-1.6.0 → tensoracle-1.6.1}/build_backend.py +0 -0
  13. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/CMakeLists.txt +0 -0
  14. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/cmake/TensoracleCubinPlugin.cmake +0 -0
  15. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/cmake/TensoracleCuda.cmake +0 -0
  16. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/include/tensoracle/cubin_plugin.hpp +0 -0
  17. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/src/cubin_plugin.cpp +0 -0
  18. {tensoracle-1.6.0 → tensoracle-1.6.1}/cpp/tests/core.cpp +0 -0
  19. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/api/bundle.md +0 -0
  20. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/api/index.md +0 -0
  21. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/api/producer.md +0 -0
  22. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/cpp-python-repositories.md +0 -0
  23. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/index.md +0 -0
  24. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/optimization.md +0 -0
  25. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/precision.md +0 -0
  26. {tensoracle-1.6.0 → tensoracle-1.6.1}/docs/tensorrt-sdk.md +0 -0
  27. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/CMakeLists.txt +0 -0
  28. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/README.md +0 -0
  29. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/bundle.cpp +0 -0
  30. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/bundle.json +0 -0
  31. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm120/bias.cubin +0 -0
  32. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm120/dot.specialized.cubin +0 -0
  33. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm120/dot.tf32-control.cubin +0 -0
  34. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm120/dot.unspecialized.cubin +0 -0
  35. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm120/scale.cubin +0 -0
  36. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm86/bias.cubin +0 -0
  37. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm86/dot.specialized.cubin +0 -0
  38. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm86/dot.tf32-control.cubin +0 -0
  39. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm86/dot.unspecialized.cubin +0 -0
  40. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm86/scale.cubin +0 -0
  41. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm89/bias.cubin +0 -0
  42. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm89/dot.specialized.cubin +0 -0
  43. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm89/dot.tf32-control.cubin +0 -0
  44. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm89/dot.unspecialized.cubin +0 -0
  45. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/bundle/cubins/sm89/scale.cubin +0 -0
  46. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/example_cubin_plugin/__init__.py +0 -0
  47. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/example_cubin_plugin/weights.safetensors +0 -0
  48. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/kernels.py +0 -0
  49. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/plugin.cpp +0 -0
  50. {tensoracle-1.6.0 → tensoracle-1.6.1}/examples/cubin_plugin/pyproject.toml +0 -0
  51. {tensoracle-1.6.0 → tensoracle-1.6.1}/mkdocs.yml +0 -0
  52. {tensoracle-1.6.0 → tensoracle-1.6.1}/pyproject.toml +0 -0
  53. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/__init__.py +0 -0
  54. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_cache.py +0 -0
  55. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_cache_format.py +0 -0
  56. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_cli.py +0 -0
  57. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_comparison.py +0 -0
  58. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_execution.py +0 -0
  59. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_memory.py +0 -0
  60. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/_plugins.py +0 -0
  61. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/bundle.py +0 -0
  62. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/convert.py +0 -0
  63. {tensoracle-1.6.0 → tensoracle-1.6.1}/src/tensoracle/producer.py +0 -0
  64. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/build_memory_runtime.py +0 -0
  65. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubin_io/check.cpp +0 -0
  66. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubin_io/kernels.cu +0 -0
  67. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubin_io/plugin.cpp +0 -0
  68. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubins/ptxas-12.9.86/sm120.cubin +0 -0
  69. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubins/ptxas-12.9.86/sm86.cubin +0 -0
  70. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/cubins/ptxas-12.9.86/sm89.cubin +0 -0
  71. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/example_runtime.py +0 -0
  72. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/native/CMakeLists.txt +0 -0
  73. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/native/export.h +0 -0
  74. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/native_build.py +0 -0
  75. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/plugin_io_runtime.py +0 -0
  76. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/precision_runtime.py +0 -0
  77. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_bundle.py +0 -0
  78. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cache_cli.py +0 -0
  79. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cmake_cuda.py +0 -0
  80. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cmake_plugin.py +0 -0
  81. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_comparison.py +0 -0
  82. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_convert.py +0 -0
  83. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cpp_core.py +0 -0
  84. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_cuda_graphs.py +0 -0
  85. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_distribution.py +0 -0
  86. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_engine_io.py +0 -0
  87. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_example_plugin.py +0 -0
  88. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_execution.py +0 -0
  89. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_plugin_io.py +0 -0
  90. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_plugins.py +0 -0
  91. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_precision.py +0 -0
  92. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_producer.py +0 -0
  93. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_shape_tensor_outputs.py +0 -0
  94. {tensoracle-1.6.0 → tensoracle-1.6.1}/tests/test_weight_hash.py +0 -0
  95. {tensoracle-1.6.0 → tensoracle-1.6.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tensoracle
3
- Version: 1.6.0
3
+ Version: 1.6.1
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
@@ -114,7 +114,7 @@ def _build_serialized_engine(
114
114
 
115
115
  register_libraries(plugins)
116
116
 
117
- builder = network = config = profile = serialized = tensor = None
117
+ builder = network = config = profile = serialized = tensor = timing = None
118
118
  inputs = {}
119
119
  try:
120
120
  builder = trt.Builder(logger or trt.Logger(trt.Logger.ERROR))
@@ -161,12 +161,22 @@ def _build_serialized_engine(
161
161
  if profile is not None:
162
162
  config.add_optimization_profile(profile)
163
163
 
164
- serialized = builder.build_serialized_network(network, config)
165
- if serialized is None:
166
- raise RuntimeError("TensorRT failed to build the serialized engine")
164
+ timing = config.create_timing_cache(b"")
165
+ if timing is None:
166
+ raise RuntimeError("TensorRT failed to create a timing cache")
167
+ if not config.set_timing_cache(timing, ignore_mismatch=False):
168
+ raise RuntimeError("TensorRT rejected the timing cache")
169
+ # A cold build can serialize large zero-filled buffers. Rebuild with the
170
+ # populated in-memory cache, releasing the first plan before the second.
171
+ for _ in range(2):
172
+ serialized = None
173
+ serialized = builder.build_serialized_network(network, config)
174
+ if serialized is None:
175
+ raise RuntimeError("TensorRT failed to build the serialized engine")
167
176
  return bytes(serialized), output_names
168
177
  finally:
169
178
  # Tensor references also keep the network alive, including in exception frames.
170
179
  inputs.clear()
171
- del tensor, profile, serialized, config, network, builder
180
+ # TensorRT requires the timing cache to outlive the builder using it.
181
+ del tensor, profile, serialized, config, network, builder, timing
172
182
  release_host_memory()
@@ -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.6.0'
22
- __version_tuple__ = version_tuple = (1, 6, 0)
21
+ __version__ = version = '1.6.1'
22
+ __version_tuple__ = version_tuple = (1, 6, 1)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -219,6 +219,12 @@ def test_tf32_is_disabled_and_other_builder_settings_are_applied(
219
219
  def set_memory_pool_limit(self, pool, limit):
220
220
  self.workspace_calls.append((pool, limit))
221
221
 
222
+ def create_timing_cache(self, data):
223
+ return object()
224
+
225
+ def set_timing_cache(self, timing, ignore_mismatch):
226
+ return True
227
+
222
228
  class Network:
223
229
  num_inputs = 1
224
230
  num_outputs = 0
@@ -68,6 +68,8 @@ def test_unavailable_host_memory_release_preserves_the_build_result(monkeypatch,
68
68
  create_builder_config=lambda: SimpleNamespace(
69
69
  clear_flag=lambda flag: None,
70
70
  set_memory_pool_limit=lambda pool, limit: None,
71
+ create_timing_cache=lambda data: object(),
72
+ set_timing_cache=lambda timing, ignore_mismatch: True,
71
73
  ),
72
74
  build_serialized_network=serialize,
73
75
  )
@@ -101,6 +101,8 @@ def test_save_large_built_plan_keeps_other_threads_running(tmp_path, monkeypatch
101
101
  create_builder_config=lambda: SimpleNamespace(
102
102
  clear_flag=lambda flag: None,
103
103
  set_memory_pool_limit=lambda pool, limit: None,
104
+ create_timing_cache=lambda data: object(),
105
+ set_timing_cache=lambda timing, ignore_mismatch: True,
104
106
  ),
105
107
  build_serialized_network=lambda network, config: plan,
106
108
  )
@@ -498,7 +500,7 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
498
500
 
499
501
  monkeypatch.setattr(trt, "get_plugin_registry", PluginRegistry)
500
502
 
501
- state = {"builds": 0, "graph_calls": 0, "shape": (1, 3, 2), "range": None}
503
+ state = {"graph_calls": 0, "shape": (1, 3, 2), "range": None}
502
504
 
503
505
  class FakeEngine:
504
506
  num_io_tensors = 2
@@ -546,6 +548,12 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
546
548
  def add_optimization_profile(self, profile):
547
549
  pass
548
550
 
551
+ def create_timing_cache(self, data):
552
+ return object()
553
+
554
+ def set_timing_cache(self, timing, ignore_mismatch):
555
+ return True
556
+
549
557
  class FakeBuilder:
550
558
  def __init__(self, logger):
551
559
  pass
@@ -560,7 +568,6 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
560
568
  return FakeProfile()
561
569
 
562
570
  def build_serialized_network(self, network, config):
563
- state["builds"] += 1
564
571
  return b"small plan"
565
572
 
566
573
  monkeypatch.setattr(trt, "Builder", FakeBuilder)
@@ -607,9 +614,9 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
607
614
  )
608
615
 
609
616
  first = open_case()
610
- assert state["builds"] == 1
617
+ assert state["graph_calls"] == 1
611
618
  assert open_case(weight_files=(same_contents,)).path == first.path
612
- assert state["builds"] == 1
619
+ assert state["graph_calls"] == 1
613
620
  variants = [
614
621
  {"name": "decoder"},
615
622
  {"graph_version": "2"},
@@ -636,12 +643,12 @@ def test_each_declared_condition_selects_a_distinct_cache_entry(tmp_path, monkey
636
643
  paths.add(open_case().path)
637
644
  assert first.path not in paths
638
645
  assert len(paths) == len(variants) + 4
639
- assert state["builds"] == len(paths) + 1
646
+ assert state["graph_calls"] == len(paths) + 1
640
647
  assert (
641
648
  open_case(graph=lambda network: pytest.fail("graph changed without version bump")).path
642
649
  == first.path
643
650
  )
644
- assert state["builds"] == len(paths) + 1
651
+ assert state["graph_calls"] == len(paths) + 1
645
652
 
646
653
 
647
654
  @pytest.mark.gpu
@@ -20,7 +20,10 @@ def open_cached(tmp_path, monkeypatch):
20
20
  builder = SimpleNamespace(
21
21
  create_network=lambda flags: SimpleNamespace(num_inputs=0, num_outputs=0),
22
22
  create_builder_config=lambda: SimpleNamespace(
23
- clear_flag=lambda flag: None, set_memory_pool_limit=lambda pool, limit: None
23
+ clear_flag=lambda flag: None,
24
+ set_memory_pool_limit=lambda pool, limit: None,
25
+ create_timing_cache=lambda data: object(),
26
+ set_timing_cache=lambda timing, ignore_mismatch: True,
24
27
  ),
25
28
  build_serialized_network=lambda network, config: b"TensorRT plan",
26
29
  )
@@ -1,15 +1,80 @@
1
- """Separate saved engines by the CUDA device properties used for compatibility."""
1
+ """Save compact engines and separate them by CUDA device compatibility."""
2
2
 
3
3
  import ctypes
4
+ import time
4
5
  from types import SimpleNamespace
5
6
 
7
+ import numpy as np
6
8
  import pytest
7
9
  import tensorrt as trt
10
+ from safetensors.numpy import save_file
8
11
 
9
12
  import tensoracle
10
13
  import tensoracle._cache as cache
11
14
 
12
15
 
16
+ @pytest.mark.gpu
17
+ @pytest.mark.req("SP-TENSORACLE-009")
18
+ def test_compact_saved_engine_executes_and_reopens_without_calling_graph(tmp_path, monkeypatch):
19
+ """A LeakyReLU engine stays below 1 MiB and computes correctly before and after reopening."""
20
+ import cupy as cp
21
+
22
+ monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))
23
+ weights = tmp_path / "weights.safetensors"
24
+ save_file({"slope": np.array([0.125], dtype=np.float16)}, weights)
25
+ shape = (1, 64, 135, 240)
26
+ graph_calls = []
27
+
28
+ def graph(network):
29
+ graph_calls.append(1)
30
+ features = network.add_input("features", trt.float16, shape)
31
+ layer = network.add_activation(features, trt.ActivationType.LEAKY_RELU)
32
+ layer.alpha = 0.125
33
+ result = layer.get_output(0)
34
+ result.name = "result"
35
+ network.mark_output(result)
36
+
37
+ def open_engine(callback):
38
+ return tensoracle.open_engine(
39
+ callback,
40
+ tensoracle.EngineBuild({"features": tensoracle.InputShape.fixed(shape)}),
41
+ name="compact-leaky-relu",
42
+ graph_version="1",
43
+ weights=(weights,),
44
+ io=tensoracle.EngineIO(
45
+ {"features": tensoracle.TensorSpec(trt.float16, shape)},
46
+ {"result": tensoracle.TensorSpec(trt.float16, shape)},
47
+ ),
48
+ )
49
+
50
+ started = time.perf_counter()
51
+ first = open_engine(graph)
52
+ elapsed = time.perf_counter() - started
53
+ size = first.path.stat().st_size
54
+ print(f"LeakyReLU saved engine: {size} bytes, build/open: {elapsed:.6f} s")
55
+ assert graph_calls == [1]
56
+ assert size < 1024 * 1024
57
+
58
+ def forbidden(network):
59
+ pytest.fail("saved engine invoked the graph callback")
60
+
61
+ reopened = open_engine(forbidden)
62
+ assert reopened.path == first.path
63
+ assert reopened.path.stat().st_size == size
64
+ assert graph_calls == [1]
65
+ host = np.linspace(-1, 1, np.prod(shape), dtype=np.float32).astype(np.float16).reshape(shape)
66
+ expected = np.where(host >= 0, host, host * np.float16(0.125))
67
+ value = cp.asarray(host)
68
+ for handle in (first, reopened):
69
+ output = cp.asarray(np.full_like(host, np.nan))
70
+ with handle.create_execution() as execution:
71
+ execution.enqueue(
72
+ {"features": value.data.ptr, "result": output.data.ptr},
73
+ stream=cp.cuda.get_current_stream().ptr,
74
+ )
75
+ np.testing.assert_array_equal(cp.asnumpy(output), expected)
76
+
77
+
13
78
  @pytest.fixture
14
79
  def cuda_driver(monkeypatch):
15
80
  properties = {
@@ -128,17 +193,18 @@ def test_changed_gpu_property_builds_a_separate_saved_engine(
128
193
  monkeypatch.setenv("XDG_CACHE_HOME", str(tmp_path / "cache"))
129
194
  builds = []
130
195
 
131
- def serialize(network, config):
196
+ def graph(network):
132
197
  builds.append(cuda_driver.device)
133
- return b"serialized engine"
134
198
 
135
199
  builder = SimpleNamespace(
136
200
  create_network=lambda flags: SimpleNamespace(num_inputs=0, num_outputs=0),
137
201
  create_builder_config=lambda: SimpleNamespace(
138
202
  clear_flag=lambda flag: None,
139
203
  set_memory_pool_limit=lambda pool, limit: None,
204
+ create_timing_cache=lambda data: object(),
205
+ set_timing_cache=lambda timing, ignore_mismatch: True,
140
206
  ),
141
- build_serialized_network=serialize,
207
+ build_serialized_network=lambda network, config: b"serialized engine",
142
208
  )
143
209
  monkeypatch.setattr(trt, "Builder", lambda logger: builder)
144
210
  monkeypatch.setattr(
@@ -153,7 +219,7 @@ def test_changed_gpu_property_builds_a_separate_saved_engine(
153
219
 
154
220
  def open_engine():
155
221
  return tensoracle.open_engine(
156
- lambda network: None,
222
+ graph,
157
223
  tensoracle.EngineBuild({}),
158
224
  name="encoder",
159
225
  graph_version="1",
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