我的 Python 代码目前面临性能问题,特别是执行速度问题。我发现了代码中的瓶颈,并且正在寻求有关如何优化它以获得更好性能的建议。
这是代码的简化版本:
import numpy as np
def merge_arrays(arr: list, new: list) -> list:
if len(arr) != len(new):
raise ValueError(f'Length of <inds> is {len(arr)} but length of <new> is {len(new)}')
else:
return transform_array(zip(arr, new), [])
def transform_array(arr: list, r: list):
for x in arr:
if isinstance(x, (list, tuple, np.ndarray)):
transform_array(x, r)
else:
r.append(x)
return r
COUNT = 100000
pips = [(np.arange(5), np.arange(5)) for _ in range(COUNT)]
b = [np.arange(50, 65) for _ in range(COUNT)]
ang = [np.arange(50, 55) for _ in range(COUNT)]
dist = [np.arange(50, 55) for _ in range(COUNT)]
result = [merge_arrays(y, [np.append(b[i][1:], [dist[i][p], ang[i][p]]) for p, i in enumerate(x)]) for x, y in pips]
处理大型数据集时会出现问题,特别是当输入列表包含大量元素时。这会导致执行时间变慢,这对我的应用程序来说是不希望的。
我尝试通过使用列表推导式并避免不必要的条件检查来优化代码,但性能仍然不令人满意。
我正在寻找有关如何重构或优化此代码以提高其性能的建议,特别是在瓶颈似乎位于的transform_array函数中。
任何见解或替代方法将不胜感激。谢谢!
主要问题是代码对
isinstance
进行了 9_500_000 次调用。对于解释的代码来说这已经太多了。更不用说类型自省永远不会很快。如果我们可以假设某些项目具有众所周知的类型并且不需要进行测试,那么可以改进代码。
Numpy 数组有一个解决方案,因为它们是类型化的,而不是 CPython 元组和 CPython 列表。请注意,将
(list, tuple, np.ndarray)
存储在变量中可使速度提高 10%。这是基于此的更快的代码:
T1 = (list, tuple)
T2 = (np.ndarray,)
def transform_array(arr: list, r: list):
for x in arr:
if isinstance(x, T1):
transform_array(x, r)
elif isinstance(x, T2):
if x.dtype == object:
transform_array(x, r)
else:
r.extend(x.tolist())
else:
r.append(x)
return r
一旦完成此优化,>90% 的时间都花在
isinstance
(尤其是第一个)或循环迭代上。
您当然可以使用 Cython 加快一点速度,以避免解释成为瓶颈,但我预计不会有很大的加速。检查 DRAM 中 2_500_000 个对象(当然不连续存储)的类型本质上很慢。