【发布时间】:2019-05-25 09:09:05
【问题描述】:
我有两个用 3D 坐标 (x, y, z) 填充的 numpy 数组。对于第一个数组(“目标”数组)的每个点,我需要找到第二个数组(“源”数组)的 4 个最近点。使用不同的方法找到实际结果没有问题,但我想尽可能加快这个过程。
我需要这个,因为我正在开发一个 Maya 工具,它将存储在网格的每个顶点中的信息传输到第二个网格,并且它们可能具有不同数量的顶点。
但在这一点上,它更像是一个 Python 问题而不是 Maya 问题,因为我的主要瓶颈是寻找顶点匹配所花费的时间。
元素的数量可以从几百到几十万不等,我想确保找到加快搜索速度的最佳方法。 我希望我的工具尽可能快,因为它可能会经常使用,而且每次运行时等待几分钟会很烦人。
我找到了一些有用的答案,让我朝着正确的方向前进:
Here 我发现了 KDTrees 和不同的算法,here 我发现了一些关于多线程的有用注意事项。
这里有一些代码模拟了我将使用的场景以及我尝试过的一些解决方案。
import timeit
import numpy as np
from multiprocessing.pool import ThreadPool
from scipy import spatial
# brut Froce
def bruteForce():
results = []
for point in sources:
dists = ((targets - [point]) ** 2).sum(axis=1) # compute distances
ndx = dists.argsort() # indirect sort
results.append(zip(ndx[:4], dists[ndx[:4]]))
return results
# Thread Pool Implementation
def threaded():
def worker(point):
dists = ((targets - [point]) ** 2).sum(axis=1) # compute distances
ndx = dists.argsort() # indirect sort
return zip(ndx[:4], dists[ndx[:4]])
pool = ThreadPool()
return pool.map(worker, sources)
# KDTree implementation
def kdTree():
tree = spatial.KDTree(targets, leafsize=50)
return [tree.query(point, k=4) for point in sources]
# define the number of points for the two arrays
n_targets = 40000
n_sources = 40000
#pick some random points
targets = np.random.rand(n_targets, 3) * 100
sources = np.random.rand(n_sources, 3) * 100
print 'KDTree: %s' % timeit.Timer(lambda: kdTree()).repeat(1, 1)[0]
print 'bruteforce: %s' % timeit.Timer(lambda: bruteForce()).repeat(1, 1)[0]
print 'threaded: %s' % timeit.Timer(lambda: threaded()).repeat(1, 1)[0]
我的结果是:
KDTree: 10.724864464 seconds
bruteforce: 211.427750433 seconds
threaded: 47.3280865123 seconds
最有希望的方法是 KDTree。
起初我认为通过使用一些线程将 KDTree 的工作拆分为单独的任务,我可以进一步加快进程。然而,在使用基本的threading.Thread 实现进行快速测试后,在线程中计算 KDTree 时,它的性能似乎更差。
阅读this scipy example我可以看到KDTrees并不是真的适合在并行线程中使用,但我并没有真正理解方式。
然后,我想知道是否有任何其他方法可以优化此代码以更快地执行,可能是通过使用多处理或其他某种技巧来并行解析我的数组。
提前感谢您的帮助!
【问题讨论】:
-
通常 Python 不擅长多线程,因为许多对数据结构的访问是由 global interpreter lock 同步的。
multiprocessing在这里可以提供帮助,但必须小心操作,以便复制数据结构并避免不必要的复制(尤其是在 Windows 上,由于缺少fork操作系统功能可能会出现问题)。 -
您能否提供一个使用 KDTree 进行多处理的示例?