为什么numpy.may_share_memory
存在?
给出确切结果的挑战是什么?
是否已弃用numpy.may_share_memory
方法?
numpy.may_share_memory
可能会给出误报,但不会给出假阴性。
numpy.shares_memory
是否给出任何假阳性且没有假阴性?
我使用numpy版本1.11.2
。
请参阅:
答案 0 :(得分:9)
添加了一个新函数
np.shares_memory
,可以准确检查两个数组是否有内存重叠。np.may_share_memory
现在也可以选择花更多的精力来减少误报。
从语义上讲,这表明较旧的may_share_memory
测试旨在让人们猜测内存是否在数组之间共享。如果肯定不是,那么可以相应地进行。如果有阳性检测(可能是假阳性),则必须小心。另一方面,新的shares_memory
函数允许精确检查。这需要更多的计算时间,但从长远来看可能是有益的,因为没有误报可以使用更多可能的优化。对may_share_memory
进行更宽松的检查可能只能保证不会返回错误的否定。
就may_share_memory
和shares_memory
的文档而言,两者都有一个关键字参数,告诉numpy用户想要检查的严格程度。
may_share_memory
:
max_work : int, optional
Effort to spend on solving the overlap problem. See shares_memory for details. Default for may_share_memory is to do a bounds check.
shares_memory
:
max_work : int, optional
Effort to spend on solving the overlap problem (maximum number of candidate solutions to consider). The following special values are recognized:
max_work=MAY_SHARE_EXACT (default)
The problem is solved exactly. In this case, the function returns True only if there is an element shared between the arrays.
max_work=MAY_SHARE_BOUNDS
Only the memory bounds of a and b are checked.
根据文档判断,这表明这两个函数可能会调用相同的底层机制,但may_share_memory
使用不太严格的默认设置进行检查。
让我们看一看at the implementation:
static PyObject *
array_shares_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
return array_shares_memory_impl(args, kwds, NPY_MAY_SHARE_EXACT, 1);
}
static PyObject *
array_may_share_memory(PyObject *NPY_UNUSED(ignored), PyObject *args, PyObject *kwds)
{
return array_shares_memory_impl(args, kwds, NPY_MAY_SHARE_BOUNDS, 0);
}
使用签名
调用相同的基础函数static PyObject *
array_shares_memory_impl(PyObject *args, PyObject *kwds, Py_ssize_t default_max_work,
int raise_exceptions)
{}
在没有深入研究源代码的情况下,我认为shares_memory
是对may_share_memory
的改进,它可以使用适当的关键字参数进行与后者相同的松散检查。旧功能可用于方便和向后兼容。
免责声明:这是我第一次查看源代码的这一部分,而我没有进一步调查array_shares_memory_impl
,所以我的印象可能完全错误。
至于两个方法之间差异的具体示例(使用默认参数调用):在上面的链接中解释了may_share_memory
仅检查数组绑定索引。如果它们与两个阵列不相交,那么没有机会它们可以共享内存。但如果它们不不相交,那么数组仍然可以是独立的!
简单示例:通过切片对连续的内存块进行不相交的分区:
>>> import numpy as np
>>> v = np.arange(6)
>>> x = v[::2]
>>> y = v[1::2]
>>> np.may_share_memory(x,y)
True
>>> np.shares_memory(x,y)
False
>>> np.may_share_memory(x,y,max_work=np.MAY_SHARE_EXACT)
False
如您所见,x
和y
是同一数组的两个不相交的切片。因此,它们的数据范围在很大程度上重叠(它们几乎相同,在内存中保存一个整数)。但是,它们的元素实际上都不相同:一个包含偶数,另一个包含原始连续块的奇数元素。所以may_share_memory
正确断言数组可以共享内存,但是在更严格的检查中发现它们没有。
至于准确计算重叠的额外难度,工作可以追溯到名为solve_may_share_memory
的工作人员,该工作人员还包含许多关于正在进行的有用的评论。简而言之,那就是
a quick check and return
如果界限没有重叠,否则MEM_OVERLAP_TOO_HARD
如果我们要求进行宽松检查(例如may_share_memory
使用默认参数),handled on the calling side为"我们不知道,所以返回{ {1}}" 因此,上面第3点的工作是True
需要额外完成的工作(或者通常是严格的检查案例)。
答案 1 :(得分:3)
在阅读以下内容之前,请阅读:
http://scipy-cookbook.readthedocs.io/items/ViewsVsCopies.html
http://www.scipy-lectures.org/advanced/advanced_numpy/
实际上问题是找到两个跨步数组a
和b
的内存重叠:
请参阅Implementatation NumPy(阅读标题中的评论非常重要。)
此问题相当于:
"找到具有正系数的有界丢番图方程的解法"
以1D阵列为例:
import numpy as np
x = np.arange(8, dtype=np.int8)
a = x[::3]
b = x[1::2]
在记忆中我们有:
1D数组是内存中的连续结构。我想我们的存储器具有64位地址(在8个字节上),并且我们的数组的每个元素具有一个字节的大小(0 <= np.int8 <256)。
要解决重叠问题,a
的一个元素的可能内存地址为:
base_a + stride_a * x_a
其中x_a
是一个变量(数组索引从0开始)。
我们对b
:
base_b + stride_b * x_b
其中x_b
是一个变量(数组索引从0开始)。
当且仅当:
时才有重叠 base_a + stride_a * x_a = base_b + stride_b * x_b
我们有:
stride_a * x_a - stride_b * x_b = base_b - base_a
0 <= x_a < shape_a
和0 <= x_b < shape_b
。
我们可以转换所有负系数,而不是阅读b
从上到下,我们可以通过变量从下到上阅读:
x_b' = shape_b - 1 - x_b
我们获得:
stride_a * x_a + stride_b * x_b = base_b + stride_b * (shape_b - 1) - base_a
这里:
3 x_a + 2 x_b = 7 (= 1 + 2 * (4 - 1))
0 <= x_a < 3
和0 <= x_b < 4
。
一个解决方案是x_a = 1
和x_b = 2
(从x_b
的底部读取)。
....
我们可以很容易地推断出对于2D数组然后是XD数组,并且每个数组元素需要多于一个字节(例如4个字节,所有数组元素都是内存中常见的大小)。
这是naive solution on my github and comparaison performance with NumPy implementation。
...