torchref-kernels 0.1.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.
@@ -0,0 +1,223 @@
1
+ # This file is automatically @generated by Cargo.
2
+ # It is not intended for manual editing.
3
+ version = 4
4
+
5
+ [[package]]
6
+ name = "crossbeam-deque"
7
+ version = "0.8.8"
8
+ source = "registry+https://github.com/rust-lang/crates.io-index"
9
+ checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a"
10
+ dependencies = [
11
+ "crossbeam-epoch",
12
+ "crossbeam-utils",
13
+ ]
14
+
15
+ [[package]]
16
+ name = "crossbeam-epoch"
17
+ version = "0.9.21"
18
+ source = "registry+https://github.com/rust-lang/crates.io-index"
19
+ checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d"
20
+ dependencies = [
21
+ "crossbeam-utils",
22
+ ]
23
+
24
+ [[package]]
25
+ name = "crossbeam-utils"
26
+ version = "0.8.23"
27
+ source = "registry+https://github.com/rust-lang/crates.io-index"
28
+ checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6"
29
+
30
+ [[package]]
31
+ name = "either"
32
+ version = "1.19.0"
33
+ source = "registry+https://github.com/rust-lang/crates.io-index"
34
+ checksum = "0e9c71c2167ca323c882b99918929403426e2373ea17242ff5653e0d5e1058be"
35
+
36
+ [[package]]
37
+ name = "heck"
38
+ version = "0.5.0"
39
+ source = "registry+https://github.com/rust-lang/crates.io-index"
40
+ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
41
+
42
+ [[package]]
43
+ name = "libc"
44
+ version = "0.2.190"
45
+ source = "registry+https://github.com/rust-lang/crates.io-index"
46
+ checksum = "ce5d3ddc6d3fa000eb1536d85e147bfe31aacaba692ed6a876f95cb7c855be78"
47
+
48
+ [[package]]
49
+ name = "multiversion"
50
+ version = "0.9.0"
51
+ source = "registry+https://github.com/rust-lang/crates.io-index"
52
+ checksum = "b4ca4bea16ffc3f443cf7d866912118196bfef4c6a1556ca00f9f9b00bb43f7c"
53
+ dependencies = [
54
+ "multiversion-macros",
55
+ ]
56
+
57
+ [[package]]
58
+ name = "multiversion-macros"
59
+ version = "0.9.0"
60
+ source = "registry+https://github.com/rust-lang/crates.io-index"
61
+ checksum = "0d416831a7317ef4b08bee00b69cbbb9c8763da7959a7026244d6266869f9c83"
62
+ dependencies = [
63
+ "proc-macro2",
64
+ "quote",
65
+ "rustversion",
66
+ "syn 3.0.7",
67
+ ]
68
+
69
+ [[package]]
70
+ name = "once_cell"
71
+ version = "1.21.4"
72
+ source = "registry+https://github.com/rust-lang/crates.io-index"
73
+ checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
74
+
75
+ [[package]]
76
+ name = "portable-atomic"
77
+ version = "1.15.0"
78
+ source = "registry+https://github.com/rust-lang/crates.io-index"
79
+ checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
80
+
81
+ [[package]]
82
+ name = "proc-macro2"
83
+ version = "1.0.107"
84
+ source = "registry+https://github.com/rust-lang/crates.io-index"
85
+ checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9"
86
+ dependencies = [
87
+ "unicode-ident",
88
+ ]
89
+
90
+ [[package]]
91
+ name = "pyo3"
92
+ version = "0.29.3"
93
+ source = "registry+https://github.com/rust-lang/crates.io-index"
94
+ checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655"
95
+ dependencies = [
96
+ "libc",
97
+ "once_cell",
98
+ "portable-atomic",
99
+ "pyo3-build-config",
100
+ "pyo3-ffi",
101
+ "pyo3-macros",
102
+ ]
103
+
104
+ [[package]]
105
+ name = "pyo3-build-config"
106
+ version = "0.29.3"
107
+ source = "registry+https://github.com/rust-lang/crates.io-index"
108
+ checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb"
109
+ dependencies = [
110
+ "target-lexicon",
111
+ ]
112
+
113
+ [[package]]
114
+ name = "pyo3-ffi"
115
+ version = "0.29.3"
116
+ source = "registry+https://github.com/rust-lang/crates.io-index"
117
+ checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330"
118
+ dependencies = [
119
+ "libc",
120
+ "pyo3-build-config",
121
+ ]
122
+
123
+ [[package]]
124
+ name = "pyo3-macros"
125
+ version = "0.29.3"
126
+ source = "registry+https://github.com/rust-lang/crates.io-index"
127
+ checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6"
128
+ dependencies = [
129
+ "proc-macro2",
130
+ "pyo3-macros-backend",
131
+ "quote",
132
+ "syn 2.0.119",
133
+ ]
134
+
135
+ [[package]]
136
+ name = "pyo3-macros-backend"
137
+ version = "0.29.3"
138
+ source = "registry+https://github.com/rust-lang/crates.io-index"
139
+ checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3"
140
+ dependencies = [
141
+ "heck",
142
+ "proc-macro2",
143
+ "quote",
144
+ "syn 2.0.119",
145
+ ]
146
+
147
+ [[package]]
148
+ name = "quote"
149
+ version = "1.0.47"
150
+ source = "registry+https://github.com/rust-lang/crates.io-index"
151
+ checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001"
152
+ dependencies = [
153
+ "proc-macro2",
154
+ ]
155
+
156
+ [[package]]
157
+ name = "rayon"
158
+ version = "1.12.0"
159
+ source = "registry+https://github.com/rust-lang/crates.io-index"
160
+ checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d"
161
+ dependencies = [
162
+ "either",
163
+ "rayon-core",
164
+ ]
165
+
166
+ [[package]]
167
+ name = "rayon-core"
168
+ version = "1.13.0"
169
+ source = "registry+https://github.com/rust-lang/crates.io-index"
170
+ checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
171
+ dependencies = [
172
+ "crossbeam-deque",
173
+ "crossbeam-utils",
174
+ ]
175
+
176
+ [[package]]
177
+ name = "rustversion"
178
+ version = "1.0.23"
179
+ source = "registry+https://github.com/rust-lang/crates.io-index"
180
+ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
181
+
182
+ [[package]]
183
+ name = "syn"
184
+ version = "2.0.119"
185
+ source = "registry+https://github.com/rust-lang/crates.io-index"
186
+ checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
187
+ dependencies = [
188
+ "proc-macro2",
189
+ "quote",
190
+ "unicode-ident",
191
+ ]
192
+
193
+ [[package]]
194
+ name = "syn"
195
+ version = "3.0.7"
196
+ source = "registry+https://github.com/rust-lang/crates.io-index"
197
+ checksum = "d62a2e0561533f2ca2561d0cf27fd9fedb640a1bf2616ff5d5c80d99017faadc"
198
+ dependencies = [
199
+ "proc-macro2",
200
+ "quote",
201
+ "unicode-ident",
202
+ ]
203
+
204
+ [[package]]
205
+ name = "target-lexicon"
206
+ version = "0.13.5"
207
+ source = "registry+https://github.com/rust-lang/crates.io-index"
208
+ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
209
+
210
+ [[package]]
211
+ name = "torchref_kernels"
212
+ version = "0.1.0"
213
+ dependencies = [
214
+ "multiversion",
215
+ "pyo3",
216
+ "rayon",
217
+ ]
218
+
219
+ [[package]]
220
+ name = "unicode-ident"
221
+ version = "1.0.26"
222
+ source = "registry+https://github.com/rust-lang/crates.io-index"
223
+ checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954"
@@ -0,0 +1,35 @@
1
+ [package]
2
+ name = "torchref_kernels"
3
+ version = "0.1.0"
4
+ edition = "2024"
5
+ # `slice::as_chunks`.
6
+ rust-version = "1.88"
7
+ license = "MIT"
8
+ description = "Prebuilt CPU kernels for TorchRef"
9
+ repository = "https://github.com/HatPdotS/TorchRef"
10
+ build = "build.rs"
11
+ readme = "README.md"
12
+
13
+ [lib]
14
+ name = "torchref_kernels"
15
+ # cdylib is the Python extension; rlib lets `cargo test` link the kernels.
16
+ crate-type = ["cdylib", "rlib"]
17
+
18
+ [dependencies]
19
+ multiversion = "0.9.0"
20
+ pyo3 = { version = "0.29.3", features = ["abi3-py310"] }
21
+ rayon = "1.12.0"
22
+
23
+ [features]
24
+ # Enabled by maturin (pyproject.toml). Off for `cargo test`, which must link libpython.
25
+ extension-module = ["pyo3/extension-module"]
26
+
27
+ [profile.release]
28
+ lto = "fat"
29
+ codegen-units = 1
30
+ opt-level = 3
31
+
32
+ # A forgotten `--release` on `maturin develop` must not produce a kernel that is
33
+ # silently 10-100x slower; debug assertions stay on so dev builds still check bounds.
34
+ [profile.dev]
35
+ opt-level = 3
@@ -0,0 +1,33 @@
1
+ Metadata-Version: 2.4
2
+ Name: torchref-kernels
3
+ Version: 0.1.0
4
+ Classifier: Programming Language :: Rust
5
+ Classifier: Programming Language :: Python :: Implementation :: CPython
6
+ Summary: Prebuilt CPU kernels for TorchRef
7
+ Author: HansPeterSeidel
8
+ License-Expression: MIT
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
11
+
12
+ # torchref-kernels
13
+
14
+ Prebuilt CPU kernels for [TorchRef](https://github.com/HatPdotS/TorchRef), written in Rust.
15
+
16
+ The wheels link neither libtorch nor a specific CPython version (abi3), so one wheel per
17
+ platform works with every torch and Python version TorchRef supports, and nothing is
18
+ compiled when TorchRef is imported. On x86-64 each kernel is built for several
19
+ microarchitecture levels and the best one for the running CPU is chosen on first use.
20
+
21
+ This package is an implementation detail of TorchRef and is installed with it; its entry
22
+ points take raw tensor pointers and are not a public API.
23
+
24
+ ## Development
25
+
26
+ ```bash
27
+ pip install -e ./kernels # or: maturin develop --release -m kernels/Cargo.toml
28
+ cargo test --manifest-path kernels/Cargo.toml
29
+ ```
30
+
31
+ Rust changes are picked up only after rebuilding. `torchref_kernels.build_info()` reports
32
+ the build profile, the selected CPU variant and a hash of the sources it was built from.
33
+
@@ -0,0 +1,21 @@
1
+ # torchref-kernels
2
+
3
+ Prebuilt CPU kernels for [TorchRef](https://github.com/HatPdotS/TorchRef), written in Rust.
4
+
5
+ The wheels link neither libtorch nor a specific CPython version (abi3), so one wheel per
6
+ platform works with every torch and Python version TorchRef supports, and nothing is
7
+ compiled when TorchRef is imported. On x86-64 each kernel is built for several
8
+ microarchitecture levels and the best one for the running CPU is chosen on first use.
9
+
10
+ This package is an implementation detail of TorchRef and is installed with it; its entry
11
+ points take raw tensor pointers and are not a public API.
12
+
13
+ ## Development
14
+
15
+ ```bash
16
+ pip install -e ./kernels # or: maturin develop --release -m kernels/Cargo.toml
17
+ cargo test --manifest-path kernels/Cargo.toml
18
+ ```
19
+
20
+ Rust changes are picked up only after rebuilding. `torchref_kernels.build_info()` reports
21
+ the build profile, the selected CPU variant and a hash of the sources it was built from.
@@ -0,0 +1,61 @@
1
+ //! Embed the build profile, optimisation level and a hash of the kernel sources, so the
2
+ //! Python side can refuse a debug build and detect a stale one.
3
+
4
+ use std::fs;
5
+ use std::path::{Path, PathBuf};
6
+
7
+ /// FNV-1a 64; `torchref.utils.native.source_hash` computes the same digest in Python.
8
+ fn fnv1a(hash: &mut u64, bytes: &[u8]) {
9
+ for b in bytes {
10
+ *hash ^= u64::from(*b);
11
+ *hash = hash.wrapping_mul(0x100_0000_01b3);
12
+ }
13
+ }
14
+
15
+ fn collect(dir: &Path, out: &mut Vec<PathBuf>) {
16
+ for entry in fs::read_dir(dir).expect("readable source dir") {
17
+ let path = entry.expect("dir entry").path();
18
+ if path.is_dir() {
19
+ collect(&path, out);
20
+ } else if path.extension().is_some_and(|e| e == "rs") {
21
+ out.push(path);
22
+ }
23
+ }
24
+ }
25
+
26
+ fn main() {
27
+ let root = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap());
28
+ let mut files = Vec::new();
29
+ collect(&root.join("src"), &mut files);
30
+ // Relative, '/'-separated and sorted, so the digest is the same on every OS.
31
+ let mut rel: Vec<(String, PathBuf)> = files
32
+ .into_iter()
33
+ .map(|p| {
34
+ let r = p
35
+ .strip_prefix(&root)
36
+ .unwrap()
37
+ .to_string_lossy()
38
+ .replace('\\', "/");
39
+ (r, p)
40
+ })
41
+ .collect();
42
+ rel.sort();
43
+ let mut hash: u64 = 0xcbf2_9ce4_8422_2325;
44
+ for (name, path) in &rel {
45
+ fnv1a(&mut hash, name.as_bytes());
46
+ fnv1a(&mut hash, &[0]);
47
+ fnv1a(&mut hash, &fs::read(path).expect("readable source"));
48
+ fnv1a(&mut hash, &[0]);
49
+ println!("cargo:rerun-if-changed={}", path.display());
50
+ }
51
+ println!("cargo:rerun-if-changed=src");
52
+ println!("cargo:rustc-env=TORCHREF_KERNELS_SOURCE_HASH={hash:016x}");
53
+ println!(
54
+ "cargo:rustc-env=TORCHREF_KERNELS_PROFILE={}",
55
+ std::env::var("PROFILE").unwrap()
56
+ );
57
+ println!(
58
+ "cargo:rustc-env=TORCHREF_KERNELS_OPT_LEVEL={}",
59
+ std::env::var("OPT_LEVEL").unwrap()
60
+ );
61
+ }
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["maturin>=1.7,<2"]
3
+ build-backend = "maturin"
4
+
5
+ [project]
6
+ name = "torchref-kernels"
7
+ dynamic = ["version"]
8
+ description = "Prebuilt CPU kernels for TorchRef"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ authors = [{name = "HansPeterSeidel"}]
13
+ classifiers = [
14
+ "Programming Language :: Rust",
15
+ "Programming Language :: Python :: Implementation :: CPython",
16
+ ]
17
+
18
+ [tool.maturin]
19
+ python-source = "python"
20
+ module-name = "torchref_kernels._native"
21
+ features = ["extension-module"]
22
+ # `pip install ./kernels` and the wheels build in release; `maturin develop` uses the dev
23
+ # profile, which Cargo.toml also optimises, so neither path yields a slow kernel.
24
+ profile = "release"
25
+
26
+ [tool.uv]
27
+ # uv only rebuilds a local package when these change; without them a Rust edit is ignored.
28
+ cache-keys = [
29
+ {file = "pyproject.toml"},
30
+ {file = "Cargo.toml"},
31
+ {file = "Cargo.lock"},
32
+ {file = "build.rs"},
33
+ {file = "src/**/*.rs"},
34
+ ]
@@ -0,0 +1,19 @@
1
+ """Prebuilt CPU kernels for TorchRef.
2
+
3
+ Low-level entry points taking ``(data_ptr, numel)`` pairs of CPU tensors. They are not
4
+ meant to be called directly: TorchRef's wrappers (``torchref.utils.native``) validate
5
+ device, dtype, contiguity and shapes, own the autograd, and choose the thread count.
6
+ Nothing here imports torch.
7
+ """
8
+
9
+ from torchref_kernels._native import ABI_VERSION, build_info
10
+ from torchref_kernels import _native
11
+
12
+ __version__ = build_info()["version"]
13
+
14
+ __all__ = ["ABI_VERSION", "build_info", "native", "__version__"]
15
+
16
+
17
+ def native():
18
+ """The compiled extension module holding the kernel entry points."""
19
+ return _native
@@ -0,0 +1,83 @@
1
+ //! The one unsafe boundary: raw `(address, length)` pairs from Python become slices.
2
+ //!
3
+ //! Python passes `tensor.data_ptr()` and `tensor.numel()` for CPU, contiguous tensors of the
4
+ //! kernel's dtype (`torchref.utils.native.buf` checks all three). Everything past this
5
+ //! module works on safe slices, and every kernel checks slice lengths against each other
6
+ //! before touching memory, so a wrong shape is a `ValueError`, not a segfault.
7
+
8
+ use pyo3::PyResult;
9
+ use pyo3::exceptions::PyValueError;
10
+ use std::ptr::NonNull;
11
+
12
+ /// `(data_ptr, numel)` of a CPU tensor, as passed from Python.
13
+ pub type Buf = (usize, usize);
14
+
15
+ fn checked<T>(buf: Buf, what: &str) -> PyResult<*mut T> {
16
+ let (addr, len) = buf;
17
+ if len == 0 {
18
+ // An empty tensor may report address 0, which is not a valid slice pointer.
19
+ return Ok(NonNull::<T>::dangling().as_ptr());
20
+ }
21
+ if addr == 0 {
22
+ return Err(PyValueError::new_err(format!("{what}: null data pointer")));
23
+ }
24
+ if addr % std::mem::align_of::<T>() != 0 {
25
+ return Err(PyValueError::new_err(format!(
26
+ "{what}: data pointer is not aligned for its dtype"
27
+ )));
28
+ }
29
+ Ok(addr as *mut T)
30
+ }
31
+
32
+ /// Borrow an input tensor's storage.
33
+ ///
34
+ /// # Safety
35
+ /// `buf` must describe `buf.1` initialised elements of `T` that stay alive and are not
36
+ /// written by anything else while the returned slice is in use.
37
+ pub unsafe fn input<'a, T>(buf: Buf, what: &str) -> PyResult<&'a [T]> {
38
+ let ptr = checked::<T>(buf, what)?;
39
+ Ok(unsafe { std::slice::from_raw_parts(ptr, buf.1) })
40
+ }
41
+
42
+ /// Borrow an output tensor's storage for writing.
43
+ ///
44
+ /// # Safety
45
+ /// As [`input`], and additionally no other slice (input or output) may overlap it.
46
+ pub unsafe fn output<'a, T>(buf: Buf, what: &str) -> PyResult<&'a mut [T]> {
47
+ let ptr = checked::<T>(buf, what)?;
48
+ Ok(unsafe { std::slice::from_raw_parts_mut(ptr, buf.1) })
49
+ }
50
+
51
+ /// Fail with a `ValueError` unless `len == expected`.
52
+ pub fn expect_len(len: usize, expected: usize, what: &str) -> PyResult<()> {
53
+ if len != expected {
54
+ return Err(PyValueError::new_err(format!(
55
+ "{what}: expected {expected} elements, got {len}"
56
+ )));
57
+ }
58
+ Ok(())
59
+ }
60
+
61
+ /// Address ranges of two buffers overlap (empty buffers never do).
62
+ fn overlaps(a: Buf, b: Buf, elem: usize) -> bool {
63
+ a.1 != 0 && b.1 != 0 && a.0 < b.0 + b.1 * elem && b.0 < a.0 + a.1 * elem
64
+ }
65
+
66
+ /// Refuse outputs that alias each other or any input; the kernels assume exclusive access.
67
+ pub fn no_aliasing(outputs: &[(Buf, &str)], inputs: &[Buf], elem: usize) -> PyResult<()> {
68
+ for (i, (o, name)) in outputs.iter().enumerate() {
69
+ for (p, _) in &outputs[i + 1..] {
70
+ if overlaps(*o, *p, elem) {
71
+ return Err(PyValueError::new_err(format!(
72
+ "{name} aliases another output"
73
+ )));
74
+ }
75
+ }
76
+ for p in inputs {
77
+ if overlaps(*o, *p, elem) {
78
+ return Err(PyValueError::new_err(format!("{name} aliases an input")));
79
+ }
80
+ }
81
+ }
82
+ Ok(())
83
+ }