Skip to content

Commit d615783

Browse files
tomwhitejakirkhamd-v-b
authored
Avoid memory copy in obstore write (#2972)
* Avoid memory copy in obstore write * Add as_bytes_like method to Buffer * Add changelog entry * No need to take unsigned bytes view following #2738 * Change method name to `as_buffer_like` --------- Co-authored-by: jakirkham <[email protected]> Co-authored-by: Davis Bennett <[email protected]>
1 parent 629b4e5 commit d615783

File tree

4 files changed

+18
-4
lines changed

4 files changed

+18
-4
lines changed

changes/2972.misc.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Avoid an unnecessary memory copy when writing Zarr with obstore

src/zarr/core/buffer/core.py

+13
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,19 @@ def as_numpy_array(self) -> npt.NDArray[Any]:
255255
"""
256256
...
257257

258+
def as_buffer_like(self) -> BytesLike:
259+
"""Returns the buffer as an object that implements the Python buffer protocol.
260+
261+
Notes
262+
-----
263+
Might have to copy data, since the implementation uses `.as_numpy_array()`.
264+
265+
Returns
266+
-------
267+
An object that implements the Python buffer protocol
268+
"""
269+
return memoryview(self.as_numpy_array()) # type: ignore[arg-type]
270+
258271
def to_bytes(self) -> bytes:
259272
"""Returns the buffer as `bytes` (host memory).
260273

src/zarr/storage/_local.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ def _put(
5252
with path.open("r+b") as f:
5353
f.seek(start)
5454
# write takes any object supporting the buffer protocol
55-
f.write(value.as_numpy_array()) # type: ignore[arg-type]
55+
f.write(value.as_buffer_like())
5656
return None
5757
else:
58-
view = memoryview(value.as_numpy_array()) # type: ignore[arg-type]
58+
view = value.as_buffer_like()
5959
if exclusive:
6060
mode = "xb"
6161
else:

src/zarr/storage/_obstore.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -161,15 +161,15 @@ async def set(self, key: str, value: Buffer) -> None:
161161

162162
self._check_writable()
163163

164-
buf = value.to_bytes()
164+
buf = value.as_buffer_like()
165165
await obs.put_async(self.store, key, buf)
166166

167167
async def set_if_not_exists(self, key: str, value: Buffer) -> None:
168168
# docstring inherited
169169
import obstore as obs
170170

171171
self._check_writable()
172-
buf = value.to_bytes()
172+
buf = value.as_buffer_like()
173173
with contextlib.suppress(obs.exceptions.AlreadyExistsError):
174174
await obs.put_async(self.store, key, buf, mode="create")
175175

0 commit comments

Comments
 (0)