如何使用Python多处理来摆脱嵌套的while循环

时间:2014-10-03 21:24:39

标签: python multithreading python-2.7

我制作了这个光线追踪器来模拟两个镜头。每次光线到达屏幕时,都会对该屏幕上的位置进行投票。但是,这种运行速度很慢。它与光线跟踪器无关,只是光线的数量(128 ^ 4)。我知道多线程不会为此工作,但我如何使用多处理来为屏幕上的像素投票。这让我大吃一惊 - 我如何让每个孩子投票给一个像素。每条射线都是独立的。这是一些示例代码。创建输入数据也存在问题。只需要将一组光线发送到多处理类就需要很长时间。

基本上,我希望内部循环产生最多8个子进程 - 让这些子进程投票 - 然后释放一个插槽......我认为即使这样也会运行缓慢。

import numpy as np
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
from matplotlib.mlab import griddata
from matplotlib import cm
import random

A = 0
B = 0
C = 0
D = 0
E_initial = np.zeros((512,512))

while A <= 50:
    B = 0
    while B <= 50:
        C = 0
        while C <= 50:
            D = 0
            while D <= 50:
                #Ray tracing here             
                x_contact = random.randint(0,511)
                y_contact = random.randint(0,511)               
                E_initial[x_contact,y_contact] += 1
                D = D + 1
            C = C +1
        B = B + 1
    print A
    A = A + 1
fig = plt.figure()
ax = fig.add_subplot(111)
plt.imshow(E_initial)
cbar = plt.colorbar(orientation='vertical')
cbar.set_label('# of contacts', rotation=270, labelpad=10)
plt.show()

1 个答案:

答案 0 :(得分:3)

在尝试多处理之前,请注意您可以消除四重while循环 通过利用一些numpy功能。

indices = np.random.randint(0, 512**2, size=(50**4))

生成0(包括)和512 ** 2(不包括)之间的随机索引位置。 每个索引位置对应于E_initial中的位置。

然后,您可以使用np.bincount来计算每个索引位置发生的次数。 np.bincount返回的数组大小为512 ** 2,并且每个位置都包含一个整数计数,对应于索引位置在indices中出现的次数。但是啊 - 这基本上就是我们希望E_initial平等的。

import numpy as np
import matplotlib.pyplot as plt

indices = np.random.randint(0, 512**2, size=(50**4))
E_initial = np.bincount(indices).reshape((512, 512))

fig = plt.figure()
ax = fig.add_subplot(111)
plt.imshow(E_initial)
cbar = plt.colorbar(orientation='vertical')
cbar.set_label('# of contacts', rotation=270, labelpad=10)
plt.show()

enter image description here

PS。有8个进程修改一个数组将会很慢,因为在分配期间必须锁定数组以避免导致错误结果的竞争条件。必须锁定每个分配将使多处理代码比等效的单进程代码慢得多。

PPS。虽然

E_initial[x_contact,y_contact] += 1

是某些语言(如C语言)的标准做法,在使用NumPy数组时,它在Python中死得很慢。它很慢,因为每个赋值涉及至少三个Python函数调用(__getitem____iadd____setitem__)。如果你把它放在四重循环中,你会得到(至少)3 * 50 ** 4个Python函数调用。如果你可以将所有Python调用减少到一个NumPy函数调用,那么你将获得巨大的性能提升。因此,如果可能的话,尽量避免将值分配给NumPy数组中的各个位置!要利用NumPy,您希望最小化函数调用和循环的数量,并尽可能少地调用NumPy函数来最大化工作量。这通常意味着形成一个大数组并将整个事物推入NumPy函数中,该函数在C或Fortran中进行计算。