Python在numpy数组之间共享指针

时间:2017-08-17 13:49:23

标签: python arrays numpy pointers shared-ptr

我请求您帮忙解决以下问题。我希望有3个numpy数组作为方案构建:

       +---+
       | a |
arr1 : +---+      +---+
       | b |  <-  | b |  
       +---+      |   |
                  +---+  : arr
       +---+      |   |
       | c |  <-  | c | 
arr2 : +---+      +---+
       | d |
       +---+

所以arr与其他数组共享其值。

arr1arr2是由示例定义的经典numpy数组:

arr1 = np.array([1,2]); arr2 = np.array([3,4])

然后,我可以通过哪种方式构建arr以便拥有行为

arr1[1] = 7  ### will give  arr1 = [1,7]   arr2 = [3,4]    arr = [7,3]
arr[1] = 13  ### will give  arr1 = [1,7]   arr2 = [13,4]   arr = [7,13]

我的测试

在网上几个小时后,ctypes功能似乎是模仿Python中C指针行为的解决方案。我试过这个:

import numpy as np
import ctypes

arr1 = np.array([1.0,0.2]).astype(np.float32)
arr2 = np.array([3.0,4.0]).astype(np.float32)
arr1_ptr = arr1.ctypes.data_as(ctypes.POINTER(ctypes.c_float))
arr1bis = np.ctypeslib.as_array((ctypes.c_float * arr1.size).from_address(ctypes.addressof(arr1_ptr.contents)))
print(arr1bis)      ####   --> [ 1.   0.2]
arr1[0] = 7.0
print(arr1bis)      ####   --> [ 7.   0.2]
arr1bis[1] = 13.0
print(arr1)         ####   --> [ 7.   13.]

所以在这里我设法在同一个数组上有2个指针。但是从几个存储器安置构建一个阵列似乎要困难得多。如果有人有想法......

感谢您的帮助。

编辑:我的测试2 - 解决方案的开始

非常感谢您的回答。 大型全局数组的想法是有用的,因为它比双重存储的懒惰解决方案便宜,并且在每次修改副本时更新副本。而且实施起来非常简单。以下性能测试代码

import numpy as np
from time import time

def benchmark(d_size,s_len):
      print("Interraction domain size: ",d_size,"   state length: ",s_len)

      arr_combined = np.arange(s_len*d_size**2).reshape((d_size,d_size,s_len))

      list_of_state = [ np.ndarray.view(arr_combined[i,j,:]) for i in range(d_size) for j in range(d_size) ]
      interraction_between_state = np.ndarray.view( arr_combined[:,:,0] )

      t0 = time()
      for state in list_of_state : state[0]+=1
      interraction_between_state*= -1
      for state in list_of_state : state[0]+=1
      print( "Exec time with np.ndarray.view: ", time()-t0 )

      list_of_state2 = [ np.arange(s_len)+s_len*(j+d_size*i) for i in range(d_size) for j in range(d_size) ]
      interraction_between_state2 = np.array( [ state[0] for state in list_of_state2 ] ).reshape((d_size,d_size))

      t0 = time()
      for state in list_of_state2 : state[0]+=1
      ### Update interraction_between_state2
      for i in range(d_size): 
          for j in range(d_size):
              interraction_between_state2[i,j]= list_of_state2[j+d_size*i][0]

      interraction_between_state2*= -1
      ### Update list_of_state2
      for i in range(d_size): 
          for j in range(d_size):
              list_of_state2[j+d_size*i][0] = interraction_between_state2[i,j]

      for state in list_of_state2 : state[0]+=1
      print("Exec time with array update:    ", time()-t0 )

benchmark(100,5)
benchmark(1000,5)
benchmark(100,50)

给出

Interraction domain size:  100    state length:  5
Exec time with np.ndarray.view:  0.00500103759765625
Exec time with array update:     0.011001110076904297
Interraction domain size:  1000    state length:  5
Exec time with np.ndarray.view:  0.5620560646057129
Exec time with array update:     1.1111111640930176
Interraction domain size:  100    state length:  50
Exec time with np.ndarray.view:  0.0060007572174072266
Exec time with array update:     0.01200103759765625

因此,即使np.ndarray.view给出的视图生成非内存连续数据表示,与数据复制的解决方案相比,性能也更好。

BUT

如果结构不一致(在维度方面),如何将它们正确地放在全局数组中?例如,如果我使用arrarr1arr2返回第一个简单示例,我将arr2替换为

arr2 = np.array([3,4,5])

为了构建全局数组,我想到了两个解决方案:

  • 将全局数组写入一行。

global_arr = np.array([1,2,3,4,5])

如果arr1arr2是二维或三维,则会丢失一些结构,并且强制事先知道全局数组和本地视图之间的索引的对应关系。此外,任何子阵列都将是内存连续的,因此在这种情况下应该要求一些补充测试来知道性能的实际增益。

  • 保留结构,并在被视为无效的单元格中添加np.nan值。

global_arr = np.array( [ [1,2,np.nan], [3,4,5 ] ] )

您对这两种解决方案有何看法?还有另一种方法可以巧妙地构建这个全局数组吗?

2 个答案:

答案 0 :(得分:1)

我认为对您的问题最简单的解决方案是创建一个包含您要操作的所有数据的大数组,然后您可以以任何方式创建view数据:< / p>

import numpy as np

arr_combined = np.array([1,2,3,4])
arr1 = np.ndarray.view(arr_combined[:2])
arr2 = np.ndarray.view(arr_combined[2:])
arr = np.ndarray.view(arr_combined[1:3])

这并不是您要求的,但它至少可以让您以您在示例中编写的方式访问数据。当然,这不仅限于四个元素的小案例,也可以用于具有数千个元素的用例。

答案 1 :(得分:0)

你不能创建一个NumPy数组,它在任意地址“指向”多个不相交的内存块。您可以构建一个大区域(arr或它的超集),然后与其他较小的数组“共享”该区域的切片。

换句话说,转过头来解决问题,首先构建大数组,最后构建小数组。然后它很容易(使用NumPy C API或简单地在Python中切割大数组)。

如果每个小数组都是一个页面的精确倍数(4 KB),你可能可以在某些操作系统中使用虚拟内存管理器进行游戏,并将它们全部重新映射为连续,但这是如此限制,以至于它可能不是不太实际。