You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When using `copy=False`, modifications via the view are visible to subsequent Quadrants kernel reads, and vice-versa. The view stays valid until the underlying storage is reallocated -- typically on `qd.init()` or `qd.reset()`, after which a fresh call to `to_torch(copy=False)` / `to_numpy(copy=False)` returns a new view.
88
+
When using `copy=False` or `copy=None` (when zero-copy succeeds), modifications via the view are visible to subsequent Quadrants kernel reads, and vice-versa. The view stays valid until the underlying storage is reallocated -- typically on `qd.init()` or `qd.reset()`, after which a fresh call to `to_torch(copy=False)` / `to_numpy(copy=False)` returns a new view.
88
89
89
90
### When zero-copy is available
90
91
@@ -105,9 +106,10 @@ On **NumPy >= 2.1**, `to_numpy(copy=False)` returns a **writable** array (via a
105
106
| Value | Behaviour |
106
107
|---|---|
107
108
|`True` (default) | Independent copy via kernel. |
109
+
|`None`| Zero-copy view via DLPack when available, otherwise falls back to a copy silently. |
108
110
|`False`| Zero-copy view via DLPack, or `ValueError` if zero-copy is unsupported for this backend/dtype. |
109
111
110
-
The default `copy=True` always returns a buffer that is safe to mutate without affecting the field/ndarray.
112
+
The default `copy=True` always returns a buffer that is safe to mutate without affecting the field/ndarray. Use `copy=None` when you want zero-copy as a best-effort optimisation without having to handle exceptions — it gives you a view when possible and a safe copy otherwise.
| Direct pass-through | no | no | yes (as kernel arg) |
310
313
311
-
The `copy` parameter is supported on `to_numpy()` and `to_torch()` for `ScalarField`, `MatrixField` (and `VectorField`), `StructField`, and all `Ndarray` types. See [Zero-copy interop via DLPack](#zero-copy-interop-via-dlpack) for the support matrix and lifetime rules.
314
+
The `copy` parameter is supported on `to_numpy()` and `to_torch()` for `ScalarField`, `MatrixField` (and `VectorField`), `StructField`, `qd.Tensor`, and all `Ndarray` types. See [Zero-copy interop via DLPack](#zero-copy-interop-via-dlpack) for the support matrix and lifetime rules.
Copy file name to clipboardExpand all lines: docs/source/user_guide/tensor.md
+27Lines changed: 27 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -151,6 +151,33 @@ a.from_torch(out)
151
151
152
152
The exact same surface is available on both backends — switching `qd.tensor(..., backend=qd.Backend.FIELD/NDARRAY)` does not require any other code change at the call site.
153
153
154
+
### Zero-copy with `copy=False`
155
+
156
+
`to_numpy()` and `to_torch()` accept a keyword-only `copy` argument:
157
+
158
+
```python
159
+
a = qd.tensor(qd.f32, shape=(1024,))
160
+
a.fill(1.0)
161
+
162
+
view = a.to_torch(copy=False) # zero-copy: aliases a's memory, or ValueError
163
+
auto = a.to_torch(copy=None) # zero-copy if possible, otherwise copy
|`True` (default) | Independent copy via kernel. Safe to mutate freely. |
170
+
|`None`| Zero-copy when available, otherwise falls back to a copy silently. |
171
+
|`False`| Zero-copy DLPack view, or `ValueError` if unsupported for this backend/dtype. |
172
+
173
+
`copy=False` and `copy=None` avoid both the buffer allocation and the copy kernel when zero-copy is available — the returned numpy array or torch tensor points directly at Quadrants' existing memory. For a large tensor this eliminates a potentially expensive memcpy and a device-side kernel launch. Writes through the view are immediately visible to subsequent Quadrants kernels (and vice versa), removing the need for `to_torch` → modify → `from_torch` round-trips.
174
+
175
+
The difference between `False` and `None`: `copy=False` raises `ValueError` when zero-copy is not supported (e.g. unsupported dtype or GPU-to-numpy), while `copy=None` silently falls back to a kernel copy in those cases. Use `copy=None` when you want zero-copy as a best-effort optimisation without having to handle exceptions.
176
+
177
+
The tradeoff of zero-copy is lifetime coupling: the view is invalidated on `qd.reset()` or `qd.init()`, and on GPU you must be mindful of stream synchronisation when both frameworks write to the same buffer.
178
+
179
+
This works identically on both backends. For the full support matrix (which backends/dtypes qualify, lifetime caveats, Metal synchronisation) see [`interop`](interop.md#zero-copy-interop-via-dlpack).
180
+
154
181
Gradient buffers behave identically: `a.grad.to_numpy()` returns the canonical view of the gradient.
0 commit comments