简体   繁体   English

使用多处理查找数组中点之间的距离

[英]Using multiprocessing to find distances between points in array

I've been trying to speed up a practice project using python's multiprocessing library.我一直在尝试使用 python 的多处理库来加速一个练习项目。 In the project I have 2 arrays, points and weights, full of x, y coordinates.在项目中,我有 2 个 arrays,点和权重,充满 x、y 坐标。 I'm trying to find the distance between each weight coordinate and point.我试图找到每个重量坐标和点之间的距离。 When I run the program with multiprocessing, the program uses all the computers ram and CPU, and when looking at task manager, up to 20 instances of python are running.当我使用多处理运行程序时,程序使用了所有计算机的内存和 CPU,当查看任务管理器时,多达 20 个 python 实例正在运行。 I know the program works because it works without multiprocessing but takes around 20 seconds to complete.我知道该程序有效,因为它无需多处理即可工作,但需要大约 20 秒才能完成。

Here is the code, and at the bottom is the programming running with Pool.map and Process from the multiprocessing library.这是代码,底部是使用 Pool.map 和多处理库中的 Process 运行的编程。

import math
import random
import multiprocessing as mp

screenSize = 1000000

pointsLength = 2000
weightLength = 20000

weightBuffer = screenSize/weightLength
pointBuffer = screenSize/pointsLength

points = []
weights = []
weightPoints = []

counter = 0

for i in range(pointsLength):
    for j in range(pointsLength):
        points.append([random.randint(j * pointBuffer, j * pointBuffer * 2), 
        random.randint(i * pointBuffer, i * pointBuffer * 2)])


for i in range(pointsLength):
    for j in range(pointsLength):
        weightPoints.append([j * weightBuffer, i * weightBuffer])
        weights.append(0)


def FindDistance(i):
    row = math.floor((i / weightLength) / (weightLength / pointsLength))
    col = math.floor((i % weightLength) / (weightLength / pointsLength))
    points1d = (pointsLength * row) + col

    dist = math.dist(points[points1d], weightPoints[i])

    weights[i] = dist

# With Multiprocessing Pool
# sumthing = []
# for i in range(len(weights)):
#     sumthing.append(i)

# with mp.Pool(4) as p:
#     p.map(FindDistance, sumthing)


# With Multiproessing Process
processes = []
for i in range(len(weights)):
    p = mp.Process(target=FindDistance, args=[i])
    p.start()
    processes.append(p)

for process in processes:
    process.join()


# Without Multiprocessing
# for i in range(len(weights)):
#     FindDistance(i)

#     counter += 1

#     if (counter % 25000 == 0):
#         print(counter / 25000)

If anyone knows how I could get multiprocessing to work, where the program would use the 8 cores on my computer without crashing the program because of ram or cpu limitations.如果有人知道我如何让多处理工作,该程序将在我的计算机上使用 8 个内核,而不会因为 ram 或 cpu 限制而使程序崩溃。

The problem is you're not doing multiprocessing correctly.问题是您没有正确进行多处理。 Specifically your code is missing the if __name__ == '__main__': guard.具体来说,您的代码缺少if __name__ == '__main__':守卫。 This is fixed in the code below which uses multiprocessing.Pool (which I think would be the best and easiest way to do what you want).这在下面使用multiprocessing.Pool的代码中得到了修复(我认为这将是做你想做的最好和最简单的方法)。 It still takes a number of seconds to execute, but it doesn't overwhelm memory and the CPUs.执行仍然需要几秒钟,但它不会压倒 memory 和 CPU。

Information about needing the if __name__ == '__main__': is buried in the Safe importing of main module subsection of The spawn and forkserver start methods section of the multiprocessing module's documentation.有关需要if __name__ == '__main__':的信息隐藏在multiprocessing模块文档spawnforkserver启动方法部分的主模块的安全导入小节中。

import math
import random
import multiprocessing as mp

screenSize = 1000000

pointsLength = 2000
weightLength = 20000

weightBuffer = screenSize/weightLength
pointBuffer = screenSize/pointsLength

points = []
weights = []
weightPoints = []

counter = 0

for i in range(pointsLength):
    for j in range(pointsLength):
        points.append([random.randint(j * pointBuffer, j * pointBuffer * 2),
        random.randint(i * pointBuffer, i * pointBuffer * 2)])


for i in range(pointsLength):
    for j in range(pointsLength):
        weightPoints.append([j * weightBuffer, i * weightBuffer])
        weights.append(0)


def FindDistance(i):
    row = math.floor((i / weightLength) / (weightLength / pointsLength))
    col = math.floor((i % weightLength) / (weightLength / pointsLength))
    points1d = (pointsLength * row) + col

    dist = math.dist(points[points1d], weightPoints[i])

    weights[i] = dist


if __name__ == '__main__':  # ADDED

    # With Multiprocessing Pool
    sumthing = []
    for i in range(len(weights)):
        sumthing.append(i)

    with mp.Pool(4) as p:
        p.map(FindDistance, sumthing)

You are iterating over the length of weights(2000 as per your code) and spawning a new process for every iteration, which means 2000 processes.您正在迭代权重的长度(根据您的代码为 2000)并为每次迭代产生一个新进程,这意味着 2000 个进程。 No wonder the CPU and RAM are full.难怪 CPU 和 RAM 已满。

What you need to do is chunk the weights array into 8 smaller arrays, ideally of equal length.您需要做的是将权重数组分成 8 个较小的 arrays,理想情况下长度相等。 Change the FindDistance function to take an array as a parameter.更改 FindDistance function 以将数组作为参数。 This parameter will be the smaller chunked array.此参数将是较小的分块数组。

def FindDistance(i_arr):
    for i in i_arr:
        row = math.floor((i / weightLength) / (weightLength / pointsLength))
        col = math.floor((i % weightLength) / (weightLength / pointsLength))
        points1d = (pointsLength * row) + col

        dist = math.dist(points[points1d], weightPoints[i])

        weights[i] = dist

def chunking(weights):
    # initialise array of empty arrays with Length equal to number of processors
    smaller_chunks = [ [] i for i in range(8) ]
    for index,item in enumerate(weights):
        index_to_push = index % 8
        smaller_chunks[index_to_push].append(item)

    return smaller_chunks
processes = []
chunks = chunking(weights)
for i in range(len(chunks)):
    p = mp.Process(target=FindDistance, args=[i])
    p.start()
    processes.append(p)

for process in processes:
    process.join()

        

The problem lies in the fact that you are looping over the length of weights .问题在于您正在遍历weights的长度。 which from your code从你的代码中

for i in range(pointsLength): #pointsLength is 2000
    for j in range(pointsLength):
        weightPoints.append([j * weightBuffer, i * weightBuffer])
        weights.append(0)

is 2000*2000 = 40,000是 2000*2000 = 40,000

So you're trying to create 40,000 new processes all at once leading your system to crash.因此,您试图同时创建 40,000 个新进程,从而导致系统崩溃。 What you can do instead is break down your list of weights into n smaller arrays and use that to just create n new processes您可以做的是将weights列表分解为n较小的 arrays 并使用它来创建n新进程

We can split the array using the numpy function numpy.array_split .我们可以使用 numpy function numpy.array_split拆分数组。 Now update the FindDistance function to accept the whole new array as an input.现在更新FindDistance function 以接受整个新数组作为输入。

def FindDistance(subarr):
    for i in subarr:
        row = math.floor((i / weightLength) / (weightLength / pointsLength))
        col = math.floor((i % weightLength) / (weightLength / pointsLength))
        points1d = (pointsLength * row) + col

        dist = math.dist(points[points1d], weightPoints[i])

        weights[i] = dist

and finally create n process with the new arguments最后用新的 arguments 创建n进程

subarrays = np.array_split(weights, n)
processes = []
for subarr in subarrays:
    p = mp.Process(target=FindDistance, args=[subarr])
    p.start()
    processes.append(p)

for process in processes:
    process.join()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM