我有一个程序进行了大量的迭代(数千到数百万到数亿)。它开始花费相当多的时间(几分钟,几天),尽管我努力优化它,但我仍然有点卡住。
个人资料:通过控制台电话使用cProfile
ncalls tottime percall cumtime percall filename:lineno(function)
500/1 0.018 0.000 119.860 119.860 {built-in method builtins.exec}
1 0.006 0.006 119.860 119.860 Simulations_profiling.py:6(<module>)
6/3 0.802 0.134 108.302 36.101 Simulations_profiling.py:702(optimization)
38147 0.581 0.000 103.411 0.003 Simulations_profiling.py:270(overlap_duo_combination)
107691 28.300 0.000 102.504 0.001 Simulations_profiling.py:225(duo_overlap)
12615015 69.616 0.000 69.616 0.000 {built-in method builtins.round}
第一个问题,是2个第一行是什么?我以为这是被调用的程序。
我将在round()
语句中通过容差比较替换if / else
方法,从而避免这种时间消耗。我想进一步优化以下两个功能,但我找不到新的方法。
import itertools
import numpy as np
class Signal:
def __init__(self, fq, t0, tf, width=0.3):
self.fq = fq # frequency in Hz
self.width = float(width) # cathodic phase width in ms
self.t0 = t0 # Instant of the first pulse in ms
self.tf = tf # End point in ms
# List of instant at which a stim pulse is triggered in ms
self.timeline = np.round(np.arange(t0, self.tf, 1/fq*1000), 3)
def find_closest_t(self, t):
val = min(self.timeline, key=lambda x:abs(x-t))
id = np.where(self.timeline==val)[0][0]
if val <= t or id == 0:
return val, id
else:
return self.timeline[id-1], id-1
def duo_overlap(S1, S2, perc):
pulse_id_t1, pulse_id_t2 = [], []
start = max(S1.t0, S2.t0) - max(S1.width, S2.width)
if start <= 0:
start = 0
start_id_S1 = 0
start_id_S2 = 0
else:
start_id_S1 = S1.find_closest_t(start)[1]
start_id_S2 = S2.find_closest_t(start)[1]
stop = min(S1.tf, S2.tf) + max(S1.width, S2.width)
for i in range(start_id_S1, len(S1.timeline)):
if S1.timeline[i] > stop:
break
for j in range(start_id_S2, len(S2.timeline)):
if S2.timeline[j] > stop:
break
d = round(abs(S2.timeline[j] - S1.timeline[i]), 3) # Computation of the distance of the 2 point
if S1.timeline[i] <= S2.timeline[j] and d < S1.width:
pulse_id_t1.append(i)
pulse_id_t2.append(j)
continue
elif S1.timeline[i] >= S2.timeline[j] and d < S2.width:
pulse_id_t1.append(i)
pulse_id_t2.append(j)
continue
else:
continue
return pulse_id_t1, pulse_id_t2
def overlap_duo_combination(signals, perc=0):
overlap = dict()
for i in range(len(signals)):
overlap[i] = list()
for subset in itertools.combinations(signals, 2):
p1, p2 = duo_overlap(subset[0], subset[1], perc = perc)
overlap[signals.index(subset[0])] += p1
overlap[signals.index(subset[1])] += p2
return overlap
该计划的说明:
我的宽度为Signal.width
且频率为Signal.fq
的方波信号从Signal.t0
开始,到Signal.tf
结束。在初始化Signal
实例期间,计算timeline
:它是一个浮点数的1D数组,其中each number corresponds to the instant at which a pulse is triggered
。
示例:的
IN: Signal(50, 0, 250).timeline
OUT: array([ 0., 20., 40., 60., 80., 100., 120., 140., 160., 180., 200., 220., 240.])
A pulse is triggered at t = 0, t = 20, t = 40, ... Each pulse has a width of 0.3.
duo_overlap()
在输入中输入2个信号(在本例中我们将保持固定为0的百分比。此函数计算S1和S2(时间线数组中的ID)脉冲的id彼此重叠。
示例:的
如果S1的脉冲从t = 0开始,脉冲从t = 0.2开始 S2,因为0.2-0 = 0.2 < 0.3(S1。宽度),2个脉冲重叠。
我尝试通过仅循环可能重叠的ID(start_id
,stop
)来优化此功能。但正如您所看到的,此功能仍然是真正的时间 - 由于通话次数过多而消费。
最后一个函数overlap_duo_combination()
将输入中的N个信号作为列表(或元组/可迭代)(实际上2 <= N <= 6)并创建dict()
,其中key是输入列表中信号的ID,该值是重叠脉冲ID列表(输入列表中信号的比较2乘2)。
示例:
输入:信号=(S1,S2,S3)S1的脉冲n°2与脉冲n°3重叠 S2的脉冲n°3与S3的脉冲n°5重叠。
输出:dict [0] = [2] / dict [1] = [3,3] / dict [2] = [5]
3为S2弹出两次,因为它将添加第一个瓦片duo_overlap()
在S1和S2上调用,第二次在S2和S3上调用它。
我不想避免重复,因为它是关于有多少不同脉冲重叠的信息(在这种情况下,2个脉冲与S2的脉冲n°3重叠)。
您是否有任何想法,建议,代码或其他任何重复代码的时间复杂性?
我目前正在研究PyCUDA的实现,因为我有一个Nvidia 1080 Ti可供使用,但我不知道C语言。 是否值得切换到GPU这个内部函数,在调用时执行并不需要很长时间,但被调用数千次?
感谢您阅读这么长的帖子,感谢您的帮助!
答案 0 :(得分:1)
如果我正确理解了您的问题,您可以依靠numpy而不是执行所有循环来加快duo_overlap()
功能。
您想从S2.timeline
中减去S1.timeline
的所有值,并将差异与信号宽度进行比较。以下函数重复S1.timeline
(重复为列)并从每行中减去S2.timeline
。因此,行索引对应于S1.timeline
,列索引对应于S2.timeline
。
def duo_overlap_np(S1, S2):
x = S1.timeline
y = S2.timeline
mat = np.repeat(x, y.shape[0]).reshape(x.shape[0],y.shape[0])
mat = mat - y
# End of S1 pulse overlaps with start of S2 pulse
overlap_1 = np.where((0 >= mat) & (mat >= -S1.width))
# End of S2 pulse overlaps with start of S1 pulse
overlap_2 = np.where((0 <= mat) & (mat <= S2.width))
# Flatten the overlap arrays. The first element returned by np.where
# corresponds to S1, the second to S2
S1_overlap = np.concatenate([overlap_1[0], overlap_2[0]])
S2_overlap = np.concatenate([overlap_1[1], overlap_2[1]])
return S1_overlap, S2_overlap
在我的机器上进行快速比较,
S1 = Signal(50, 0, 1000, width=0.3)
S2 = Signal(25.5, 20.2, 1000, width=0.6)
%timeit duo_overlap(S1, S2, 0)
# 7 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit duo_overlap_np(S1, S2)
# 38.2 µs ± 2.42 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)