写时复制#
写时复制是一种内存管理策略,允许多个包含相同数据的cuDF对象引用相同的内存地址,只要它们都不修改底层数据。
通过这种方法,任何生成对象未修改视图的操作(如复制、切片或像DataFrame.head这样的方法)都会返回一个新对象,该对象指向与原始对象相同的内存。
然而,当现有对象或新对象被修改时,在修改之前会复制数据,确保更改不会在两个对象之间传播。
通过查看以下示例,可以最好地理解这种行为。
cuDF 中的默认行为是禁用写时复制,因此要使用它,必须通过设置 cuDF 选项明确选择启用。建议在脚本执行开始时设置写时复制,因为在脚本执行过程中更改此设置时,会出现意外行为,即在启用写时复制时创建的对象仍将具有写时复制行为,而在禁用写时复制时创建的对象将具有不同的行为。
启用写时复制#
使用
cudf.set_option:>>> import cudf >>> cudf.set_option("copy_on_write", True)
在启动Python解释器之前,将环境变量
CUDF_COPY_ON_WRITE设置为1:export CUDF_COPY_ON_WRITE="1" python -c "import cudf"
禁用写时复制#
可以通过将copy_on_write选项设置为False来禁用写时复制:
>>> cudf.set_option("copy_on_write", False)
制作副本#
使用写时复制不需要在代码中进行额外的更改。
>>> series = cudf.Series([1, 2, 3, 4])
执行浅拷贝将创建一个新的Series对象,指向相同的底层设备内存:
>>> copied_series = series.copy(deep=False)
>>> series
0 1
1 2
2 3
3 4
dtype: int64
>>> copied_series
0 1
1 2
2 3
3 4
dtype: int64
当对series或copied_series执行写操作时,会创建一个真实的数据物理副本:
>>> series[0:2] = 10
>>> series
0 10
1 10
2 3
3 4
dtype: int64
>>> copied_series
0 1
1 2
2 3
3 4
dtype: int64
注释#
当启用写时复制时,切片或索引时不再有视图的概念。从这个意义上说,索引的行为就像人们期望的内置Python容器(如lists)一样,而不是索引numpy arrays。修改由cuDF创建的“视图”将始终触发复制,并且不会修改原始对象。
写时复制产生了更加一致的复制语义。由于每个对象都是原始对象的副本,用户不再需要考虑何时可能会意外地在原地发生修改。这将使操作之间保持一致,并在启用写时复制时使cudf和pandas的行为保持一致。以下是一个例子,展示了在没有启用写时复制的情况下,pandas和cudf目前的不一致行为:
>>> import pandas as pd
>>> s = pd.Series([1, 2, 3, 4, 5])
>>> s1 = s[0:2]
>>> s1[0] = 10
>>> s1
0 10
1 2
dtype: int64
>>> s
0 10
1 2
2 3
3 4
4 5
dtype: int64
>>> import cudf
>>> s = cudf.Series([1, 2, 3, 4, 5])
>>> s1 = s[0:2]
>>> s1[0] = 10
>>> s1
0 10
1 2
>>> s
0 1
1 2
2 3
3 4
4 5
dtype: int64
当启用写时复制时,上述不一致性得到解决:
>>> import pandas as pd
>>> pd.set_option("mode.copy_on_write", True)
>>> s = pd.Series([1, 2, 3, 4, 5])
>>> s1 = s[0:2]
>>> s1[0] = 10
>>> s1
0 10
1 2
dtype: int64
>>> s
0 1
1 2
2 3
3 4
4 5
dtype: int64
>>> import cudf
>>> cudf.set_option("copy_on_write", True)
>>> s = cudf.Series([1, 2, 3, 4, 5])
>>> s1 = s[0:2]
>>> s1[0] = 10
>>> s1
0 10
1 2
dtype: int64
>>> s
0 1
1 2
2 3
3 4
4 5
dtype: int64
显式深拷贝和浅拷贝比较#
启用写时复制 |
禁用写时复制(默认) |
|
|---|---|---|
|
创建了一个真实的副本,更改不会传播到原始对象。 |
创建了一个真实的副本,更改不会传播到原始对象。 |
|
内存在这两个对象之间共享,但在一个对象上执行任何写操作之前,都会触发真正的物理复制。因此,更改不会传播到原始对象。 |
内存在这两个对象之间共享,对一个对象所做的更改将传播到另一个对象。 |