Python numpy 数组之间的共享指针

Python shared pointer between numpy array

请您帮忙解决以下问题。我希望按照方案构建 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 给出的视图生成非内存连续数据表示,与数据重复的解决方案相比,性能也更好。

但是

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

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

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

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

如果 arr1arr2 是二维或三维的,那会丢失一些结构,并且强制提前知道全局数组和局部视图之间索引的对应关系。此外,任何子数组都是内存连续的,因此需要进行一些补充测试才能了解在这种情况下性能的真正提升。

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

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

您不能创建一个 NumPy 数组,其中 "points to" 多个不相交的内存块位于任意地址。您可以构建一个大区域(arr 或其超集),然后 "share" 该区域的切片与其他较小的数组。

换句话说,把问题倒过来,先建大阵,后建小阵。然后很容易(使用 NumPy C API 或简单地将大数组切片 Python)。

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

我认为解决您问题的最简单方法是创建一个大数组,其中包含您要操作的所有数据,然后您可以创建 views 数据,以任何您想要的方式对其进行分组:

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])

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