Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring of Buffers (last step towards unifying COW and Spilling) #13801

Merged
merged 30 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4273e04
Introduce and use BufferOwner
madsbk Aug 2, 2023
6201b6a
rename ExposureTrackedBuffer => ExposureTrackedBufferOwner
madsbk Aug 2, 2023
06ab42e
rename SpillableBuffer => SpillableBufferOwner
madsbk Aug 2, 2023
fa076ef
fix test_buffer_creation_from_any
madsbk Aug 2, 2023
b37425c
doc
madsbk Aug 3, 2023
d6fc8b4
clean up inits
madsbk Aug 3, 2023
3b49970
unify as_spillable_buffer, as_exposure_tracked_buffer, and as_buffer
madsbk Aug 3, 2023
25603ef
Merge branch 'branch-23.10' into buffer_owner
madsbk Nov 2, 2023
a9c38d9
Merge branch 'branch-23.12' into buffer_owner
madsbk Nov 3, 2023
3196670
Apply suggestions from code review
madsbk Nov 23, 2023
de0cc61
Merge branch 'branch-24.02' into buffer_owner
madsbk Nov 23, 2023
1f82e1b
Update python/cudf/cudf/core/buffer/utils.py
madsbk Nov 23, 2023
d22370c
fix errror check
madsbk Nov 24, 2023
3053e3a
clean up __str__ and __repr__
madsbk Nov 24, 2023
63382d9
Update python/cudf/cudf/core/buffer/buffer.py
madsbk Nov 24, 2023
ba1bd1e
Merge branch 'buffer_owner' of github.com:madsbk/cudf into buffer_owner
madsbk Nov 24, 2023
bd59f7c
type hint clean up
madsbk Nov 24, 2023
411590d
test_buffer_str
madsbk Nov 24, 2023
f450e28
BufferOwner: now has exposed and mark_exposed
madsbk Nov 24, 2023
c423812
doc
madsbk Dec 13, 2023
2b70e7a
Merge branch 'branch-24.02' of github.com:rapidsai/cudf into buffer_o…
madsbk Dec 14, 2023
d81cd3b
merge `ExposureTrackedBufferOwner` and `BufferOwner`
madsbk Dec 14, 2023
89b45fd
spill serialize now returns a Buffer
madsbk Dec 14, 2023
a428f15
Merge branch 'branch-24.02' of github.com:rapidsai/cudf into buffer_o…
madsbk Jan 12, 2024
5ffba0c
doc
madsbk Jan 12, 2024
1606ccf
Merge branch 'buffer_owner' of github.com:madsbk/cudf into buffer_owner
madsbk Jan 12, 2024
c7d6378
Buffer.memoryview(): removed the size and offset arguments
madsbk Jan 12, 2024
d1cb966
Merge branch 'branch-24.02' into buffer_owner
madsbk Jan 12, 2024
a00a12b
copyrights
madsbk Jan 12, 2024
6a9597a
moved get_buffer_owner to utils.py
madsbk Jan 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/cudf/source/developer_guide/library_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,26 +325,26 @@ This section describes the internal implementation details of the copy-on-write
It is recommended that developers familiarize themselves with [the user-facing documentation](copy-on-write-user-doc) of this functionality before reading through the internals
below.

The core copy-on-write implementation relies on the factory function `as_exposure_tracked_buffer` and the two classes `ExposureTrackedBuffer` and `BufferSlice`.
The core copy-on-write implementation relies on `ExposureTrackedBuffer` and the tracking features of `BufferOwner`.

An `ExposureTrackedBuffer` is a subclass of the regular `Buffer` that tracks internal and external references to its underlying memory. Internal references are tracked by maintaining [weak references](https://docs.python.org/3/library/weakref.html) to every `BufferSlice` of the underlying memory. External references are tracked through "exposure" status of the underlying memory. A buffer is considered exposed if the device pointer (integer or void*) has been handed out to a library outside of cudf. In this case, we have no way of knowing if the data are being modified by a third party.
`BufferOwner` tracks internal and external references to its underlying memory. Internal references are tracked by maintaining [weak references](https://docs.python.org/3/library/weakref.html) to every `ExposureTrackedBuffer` of the underlying memory. External references are tracked through "exposure" status of the underlying memory. A buffer is considered exposed if the device pointer (integer or void*) has been handed out to a library outside of cudf. In this case, we have no way of knowing if the data are being modified by a third party.

`BufferSlice` is a subclass of `ExposureTrackedBuffer` that represents a _slice_ of the memory underlying a exposure tracked buffer.
`ExposureTrackedBuffer` is a subclass of `Buffer` that represents a _slice_ of the memory underlying an exposure tracked buffer.

When the cudf option `"copy_on_write"` is `True`, `as_buffer` calls `as_exposure_tracked_buffer`, which always returns a `BufferSlice`. It is then the slices that determine whether or not to make a copy when a write operation is performed on a `Column` (see below). If multiple slices point to the same underlying memory, then a copy must be made whenever a modification is attempted.
When the cudf option `"copy_on_write"` is `True`, `as_buffer` returns a `ExposureTrackedBuffer`. It is this class that determines whether or not to make a copy when a write operation is performed on a `Column` (see below). If multiple slices point to the same underlying memory, then a copy must be made whenever a modification is attempted.


### Eager copies when exposing to third-party libraries

If a `Column`/`BufferSlice` is exposed to a third-party library via `__cuda_array_interface__`, we are no longer able to track whether or not modification of the buffer has occurred. Hence whenever
If a `Column`/`ExposureTrackedBuffer` is exposed to a third-party library via `__cuda_array_interface__`, we are no longer able to track whether or not modification of the buffer has occurred. Hence whenever
someone accesses data through the `__cuda_array_interface__`, we eagerly trigger the copy by calling
`.make_single_owner_inplace` which ensures a true copy of underlying data is made and that the slice is the sole owner. Any future copy requests must also trigger a true physical copy (since we cannot track the lifetime of the third-party object). To handle this we also mark the `Column`/`BufferSlice` as exposed thus indicating that any future shallow-copy requests will trigger a true physical copy rather than a copy-on-write shallow copy.
`.make_single_owner_inplace` which ensures a true copy of underlying data is made and that the slice is the sole owner. Any future copy requests must also trigger a true physical copy (since we cannot track the lifetime of the third-party object). To handle this we also mark the `Column`/`ExposureTrackedBuffer` as exposed thus indicating that any future shallow-copy requests will trigger a true physical copy rather than a copy-on-write shallow copy.

### Obtaining a read-only object

A read-only object can be quite useful for operations that will not
mutate the data. This can be achieved by calling `.get_ptr(mode="read")`, and using `cuda_array_interface_wrapper` to wrap a `__cuda_array_interface__` object around it.
This will not trigger a deep copy even if multiple `BufferSlice` points to the same `ExposureTrackedBuffer`. This API should only be used when the lifetime of the proxy object is restricted to cudf's internal code execution. Handing this out to external libraries or user-facing APIs will lead to untracked references and undefined copy-on-write behavior. We currently use this API for device to host
This will not trigger a deep copy even if multiple `ExposureTrackedBuffer`s point to the same `ExposureTrackedBufferOwner`. This API should only be used when the lifetime of the proxy object is restricted to cudf's internal code execution. Handing this out to external libraries or user-facing APIs will lead to untracked references and undefined copy-on-write behavior. We currently use this API for device to host
copies like in `ColumnBase.data_array_view(mode="read")` which is used for `Column.values_host`.


Expand Down
10 changes: 8 additions & 2 deletions python/cudf/cudf/core/abc.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2020-2022, NVIDIA CORPORATION.
# Copyright (c) 2020-2024, NVIDIA CORPORATION.
"""Common abstract base classes for cudf."""

import pickle
Expand Down Expand Up @@ -89,7 +89,13 @@ def device_serialize(self):
"""
header, frames = self.serialize()
assert all(
isinstance(f, (cudf.core.buffer.Buffer, memoryview))
isinstance(
f,
(
cudf.core.buffer.Buffer,
memoryview,
),
)
for f in frames
)
header["type-serialized"] = pickle.dumps(type(self))
Expand Down
8 changes: 6 additions & 2 deletions python/cudf/cudf/core/buffer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Copyright (c) 2022-2023, NVIDIA CORPORATION.
# Copyright (c) 2022-2024, NVIDIA CORPORATION.

from cudf.core.buffer.buffer import Buffer, cuda_array_interface_wrapper
from cudf.core.buffer.buffer import (
Buffer,
BufferOwner,
cuda_array_interface_wrapper,
)
from cudf.core.buffer.exposure_tracked_buffer import ExposureTrackedBuffer
from cudf.core.buffer.spillable_buffer import SpillableBuffer, SpillLock
from cudf.core.buffer.utils import (
Expand Down
Loading