是将独立块填充到全局数组中的“进程”或“线程”策略?

时间:2019-02-14 19:34:34

标签: python arrays multithreading numpy

在python2中,我想通过使用并行进程(或线程)填充不同的子数组(总共16个块)来填充全局数组。我必须精确说明每个块都不依赖于其他块,这是指我在执行当前块的每个单元格的分配时。

1)从我发现的结果来看,通过使用不同的“ processes”,我将从CPU多核中受益匪浅,但是要由所有其他进程共享全局数组似乎有些复杂。

2)从另一个角度来看,由于实现起来不太困难,因此我可以使用“ threads”而不是“ processes”。我发现“ ThreadPool”中的“ multiprocessing.dummy”库允许所有其他并发线程共享此全局数组。

例如,对于python2.7,以下代码有效:

from multiprocessing.dummy import Pool as ThreadPool

## discretization along x-axis and y-axis for each block
arrayCross_k = np.linspace(kMIN, kMAX, dimPoints)
arrayCross_mu = np.linspace(-1, 1, dimPoints)
# Build all big matrix with N total blocks = dimBlock*dimBlock = 16 here
arrayFullCross = np.zeros((dimBlocks, dimBlocks, arrayCross_k.size, arrayCross_mu.size))
dimBlocks = 4
# Size of dimension along k and mu axis
dimPoints = 100
# dimension along one dimension of global arrayFullCross
dimMatCovCross = dimBlocks*dimPoints

# Build cross-correlation matrix 
def buildCrossMatrix_loop(params_array):
  # rows indices
  xb = params_array[0]
  # columns indices
  yb = params_array[1]
  # Current redshift
  z = zrange[params_array[2]]
  # Loop inside block
  for ub in range(dimPoints):
    for vb in range(dimPoints):
      # Diagonal blocs 
      if (xb == yb):
      # Fill the (xb,yb) su-block of global array by 
        arrayFullCross[xb][xb][ub][vb] = 2*P_obs_cross(arrayCross_k[ub], arrayCross_mu[vb] , z, 10**P_m(np.log10(arrayCross_k[ub])),

        ...
        ...

# End of function buildCrossMatrix_loop

# Main loop
while i < len(zrange):

  def generatorCrossMatrix(index):
    for igen in range(dimBlocks):
      for lgen in range(dimBlocks):
        yield igen, lgen, index

if __name__ == '__main__':

  # Use 20 threads
  pool = ThreadPool(20)
  pool.map(buildCrossMatrix_loop, generatorCrossMatrix(i))

  # Increment index "i"
  i = i+1

但是不幸的是,即使使用20个线程,我也意识到我的CPU内核没有完全运行(实际上,使用'top'或'htop'命令,我只能看到100%的单个进程)。

3)如果要充分利用CPU的16个内核,我必须选择什么策略(例如pool.map(function, generator)) but with also the sharing of global array就是这种情况?

4)有人告诉我对每个子数组进行I / O(基本上,将每个块写入文件中,并通过读取它们收集所有子数组并填充完整的数组)。此解决方案很方便,但我想避免使用I / O(除非确实没有其他解决方案)。

5)我已经用MPI library来练习C language,并且填充子数组并最终收集它们以构建一个大数组的操作不是很复杂。但是,我不想将MPI与Python语言一起使用(即使存在,我也不知道)。

6)我还尝试将Process的目标使用与我的填充函数(buildCrossMatrix_loop)相同的目标,例如上述while的主循环中:

from multiprocessing import Process

# Main loop on z range
while i < len(zrange):

  params_p = []
  for ip in range(4):
    for jp in range(4):
      params_p.append(ip)
      params_p.append(jp)
      params_p.append(i)
      p = Process(target=buildCrossMatrix_loop, args=(params_p,))
      params_p = []
      p.start()

  # Finished : wait everybody
  p.join()

  ...
  ...

  i = i+1
  # End of main while loop

但是最终的2D全局数组仅填充零。因此,我必须推断出Process函数不会共享要填充的数组?

7)那么我必须寻找哪种策略? :

1。使用“池进程”并找到一种共享全局阵列的方法,知道我所有的16核都将运行

2。使用“线程”并共享全局数组,但是乍一看,性能似乎不如“池进程”好。也许有一种方法可以提高每个“线程”的功能,就像使用“池进程”一样?

我尝试遵循https://docs.python.org/2/library/multiprocessing.html上的不同示例,但是,从加速的角度来看,这没有成功,也就是说没有成功。

我认为,主要问题是所有子数组的聚集,或者其他进程或线程不共享全局数组arrayFullCross < / p>

如果有人在多线程上下文(这里是数组)中有一个共享全局变量的简单示例,那么将其放在这里会很好。

更新1:我使用Threading(而不是multiprocessing)进行了测试,但性能仍然很差。 GIL显然没有被解锁,即htop命令中仅出现一个进程(也许Threading库的版本不是正确的)。

因此,我将尝试使用“ 返回”方法来解决我的问题。

天真的,我试图在应用map函数的函数的末尾返回整个数组,如下所示:

# Build cross-correlation matrix 
def buildCrossMatrix_loop(params_array):

  # rows indices
  xb = params_array[0]
  # columns indices
  yb = params_array[1]
  # Current redshift
  z = zrange[params_array[2]]
  # Loop inside block
  for ub in range(dimPoints):
    for vb in range(dimPoints):
      # Diagonal blocs 
      if (xb == yb):         
        arrayFullCross[xb][xb][ub][vb] = 2*P_obs_cross(arrayCross_k[ub], arrayCross_mu[vb])

      ... 
      ... #others assignments on arrayFullCross elements

  # Return global array to main process
  return arrayFullCross

然后,我尝试像这样从map接收此全局数组:

if __name__ == '__main__':

  pool = Pool(16)
  outputArray = pool.map(buildCrossMatrix_loop, generatorCrossMatrix(i))
  pool.terminate()
  ## Print outputArray
  print 'outputArray = ', outputArray

  ## Reshape 4D outputArray to 2D array
  arrayFullCross2D_swap = np.array(outputArray).swapaxes(1,2).reshape(dimMatCovCross,dimMatCovCross)

不幸的是,当我打印outputArray时,我得到了:

outputArray =  [None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]

这不是4D outputArray所期望的,只是16个列表的无(我认为16个的数量与generatorCrossMatrix(i)提供的进程数量相对应。)

一旦map启动并完成后,我如何取回整个4D阵列?

1 个答案:

答案 0 :(得分:0)

首先,我相信multiprocessing.ThreadPool是私有API,因此应避免使用它。现在multiprocessing.dummy是一个无用的模块。它不会执行任何多线程/处理,这就是为什么您看不到任何好处的原因。您应该使用“普通” multiprocessing模块。

第二个代码不起作用,因为它使用了多个进程。流程不共享内存,因此您在子流程中所做的更改不会反映在其他子流程或主流程中。您要么想要:

  • 返回值并将其结合到主过程中,例如使用multiprocessing.Pool.map
  • 使用threading代替multiprocessing: just replace导入多处理with导入线程and多重处理。进程with线程。 li>

请注意,threading版本仅在上可用,因为numpy在计算过程中会释放GIL,否则它将停留在1个CPU上。

您可能要看this similar question几分钟前的I answered