[英]Python, efficient paralell operation using a dict
首先抱歉我的英语不够完美。
我想,我的问题很容易解释。
result={}
list_tuple=[(float,float,float),(float,float,float),(float,float,float)...]#200k tuples
threshold=[float,float,float...] #max 1k values
for tuple in list_tuple:
for value in threeshold:
if max(tuple)>value and min(tuple)<value:
if value in result:
result[value].append(tuple)
else:
result[value]=[]
result[value].append(tuple)
list_tuple包含大约200k元组,我必须非常快速地执行此操作(在普通PC上最多2/3秒)。
我的第一个尝试是使用prange()在cython中执行此操作(因此我可以从cython优化和并行执行中获益),但问题是(一如既往),GIL:在prange()中我可以管理列表和使用cython memviews的元组,但是我不能在dict中插入我的结果。
在cython中我也尝试使用c ++ std的unordered_map,但现在的问题是我无法在c ++中创建数组向量(这将是我的dict的值)。
第二个问题是类似的:
list_tuple=[((float,float),(float,float)),((float,float),(float,float))...]#200k tuples of tuples
result={list_tuple[0][0]:[]}
for tuple in list_tuple:
if tuple[0] in result:
result[tuple[0]].append(tuple)
else:
result[tuple[0]]=[]
这里我还有另一个问题,如果想要使用prange()我必须使用自定义散列函数来使用数组作为c ++ unordered_map的键
正如您所看到的,我的片段非常简单,可以在并列中运行。
我想尝试使用numba,但可能会因为GIL而相同,我更喜欢使用cython,因为我需要一个二进制代码(这个库可能是商业软件的一部分,所以只允许使用二进制库)。
一般来说我想避免使用c / c ++函数,我希望找到一种方法来管理像dicts / list这样的东西,并且在Python域中尽可能地保留cython性能; 但我对所有建议持开放态度。
谢谢
由于该方法基本上在数据样本和阈值之间执行外部产品,因此显着增加了所需的存储器,这可能是不期望的。 这里可以找到改进的方法。 我保留这个答案以供将来参考,因为在这个答案中提到了它。
我发现与OP的代码相比,性能提高了~ 20
倍。
这是一个使用numpy
的例子。 数据是矢量化的,操作也是如此。 请注意,生成的dict包含空列表,与OP的示例相反,因此可能需要额外的清理步骤(如果适用)。
import numpy as np
# Data setup
data = np.random.uniform(size=(200000, 3))
thresh = np.random.uniform(size=1000)
# Compute tuples for thresholds.
condition = (
(data.min(axis=1)[:, None] < thresh)
& (data.max(axis=1)[:, None] > thresh)
)
result = {v: data[c].tolist() for c, v in zip(condition.T, thresh)}
@ a_guest的代码:
def foo1(data, thresh):
data = np.asarray(data)
thresh = np.asarray(thresh)
condition = (
(data.min(axis=1)[:, None] < thresh)
& (data.max(axis=1)[:, None] > thresh)
)
result = {v: data[c].tolist() for c, v in zip(condition.T, thresh)}
return result
此代码为thresh
每个项创建一次字典条目。
OP代码,使用default_dict
(来自collections
)简化了一下:
def foo3(list_tuple, threeshold):
result = defaultdict(list)
for tuple in list_tuple:
for value in threeshold:
if max(tuple)>value and min(tuple)<value:
result[value].append(tuple)
return result
这个为符合条件的每个项目更新一次字典条目。
并用他的样本数据:
In [27]: foo1(data,thresh)
Out[27]: {0: [], 1: [[0, 1, 2]], 2: [], 3: [], 4: [[3, 4, 5]]}
In [28]: foo3(data.tolist(), thresh.tolist())
Out[28]: defaultdict(list, {1: [[0, 1, 2]], 4: [[3, 4, 5]]})
时间测试:
In [29]: timeit foo1(data,thresh)
66.1 µs ± 197 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
# In [30]: timeit foo3(data,thresh)
# 161 µs ± 242 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [31]: timeit foo3(data.tolist(),thresh.tolist())
30.8 µs ± 56.4 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
对数组的迭代比使用列表慢。 tolist()
时间很tolist()
; 列表的np.asarray
更长。
使用更大的数据样本, array
版本更快:
In [42]: data = np.random.randint(0,50,(3000,3))
...: thresh = np.arange(50)
In [43]:
In [43]: timeit foo1(data,thresh)
16 ms ± 391 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [44]: %%timeit x,y = data.tolist(), thresh.tolist()
...: foo3(x,y)
...:
83.6 ms ± 68.6 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
通过使用numpy
的矢量化功能,可以实现多项性能改进:
min
和max
。 相反,它们可以预先计算,然后针对每个阈值重复使用。 list_tuple
)在纯Python中执行。 可以使用numpy
对此循环进行矢量化。 在以下测试中,我使用了data.shape == (200000, 3); thresh.shape == (1000,)
data.shape == (200000, 3); thresh.shape == (1000,)
如OP中所示。 我也省略了对result
dict
修改,因为根据数据,这可能会快速溢出内存。
v_min = [min(t) for t in data]
v_max = [max(t) for t in data]
for mi, ma in zip(v_min, v_max):
for value in thresh:
if ma > value and mi < value:
pass
与OP的代码相比,这会使性能提高~ 5
。
v_min = data.min(axis=1)
v_max = data.max(axis=1)
mask = np.empty(shape=(data.shape[0],), dtype=bool)
for t in thresh:
mask[:] = (v_min < t) & (v_max > t)
samples = data[mask]
if samples.size > 0:
pass
与OP的代码相比,性能提高了~ 30
。 这种方法的另一个好处是它不包含对列表的增量append
,这可能会降低程序的速度,因为可能需要重新分配内存。 相反,它会在一次尝试中创建每个列表(每个阈值)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.