还有比这更好的运行时分析方法吗?

计算科学 Python 数字 数据分析 随机数生成 随机抽样
2021-12-25 03:10:14

我目前有 2 个不同的函数,它们带有矢量化选项:我正在尝试将它们的运行时间与计算的迭代次数进行比较acc_rej_sine(max_iter, algorithm=None)analytical_sine(max_iter, algorithm=None)即比较所有4种方法;2 个循环,2 个矢量化。基本上我的代码是这样的:

def analytical_sine(max_iter, algorithm=None):
    if algorithm is None:
        count = 0
        analytical_hist = []
        for i in range(max_iter):
            count += 1
            progress = round((count/max_iter)*100)
            sys.stdout.write('\r' + str(progress) + '%')
            uni_dist = np.random.uniform(0, 1)
            arccos = np.arccos(1 - 2*uni_dist)
            analytical_hist.append(arccos)
    elif algorithm is "vectorise":
        analytical_hist = np.arccos(1 - 2*np.random.uniform(0, 1, max_iter))

    return analytical_hist

def acc_rej_sine(max_iter, algorithm=None):
    x = np.random.uniform(0, np.pi, max_iter)
    y = np.random.rand(max_iter)
    if algorithm is None:
        accepted_x = []
        j = count = 0
        for i in range(max_iter):
            count += 1
            progress = round((count/max_iter)*100)
            sys.stdout.write('\r' + str(progress) + '%')
            if y[i] <= np.sin(x[i]):
                accepted_x.append(x[i])
                j += 1
    elif algorithm is "vectorise":
        accepted_x = np.extract((y <= np.sin(x)), x)

    return accepted_x

def runtime(func, runs, max_iter, algorithm=None):
    time = []
    for i in range(runs):
        start = timer()
        func()
        end = timer()
        time.append((end-start))
    error = np.std(time)
    time = sum(time)/runs

    return time, error

def time_analysis():
    time1, time2, time3, time4 = [], [], [], []
    error1, error2, error3, error4 = [], [], [], []
    for i in np.arange(1, 8, 1):
        max_iter = 10**i
        time, error = runtime(analytical_sine, 5, int(max_iter))
        time1.append(time)
        error1.append(error)
        time, error = runtime(analytical_sine, 5, int(max_iter), "vectorise")
        time2.append(time)
        error2.append(error)
        time, error = runtime(acc_rej_sine, 5, int(max_iter))
        time3.append(time)
        error3.append(error)
        time, error = runtime(acc_rej_sine(max_iter), 5, int(max_iter), "vectorise")
        time4.append(time)
        error4.append(error)
    return [time1, time2, time3, time4], [error1, error2, error3, error4]

# to run the code I would probably do something like this
time, error = time_analysis()
#then if I wanna plot number of iterations vs run time with errors I would probably do something along the lines of
plt.plot(max_iter, time) # max_iter would be a list of [10**i for i in np.arange(1, 8, 1)]
plt.errorbar(error)

所以我的想法是我的runtime()函数将允许我放入我想要比较的 4 个函数中的任何一个(目前还没有工作,我不能/还没有弄清楚为什么),并运行它 5次,计算出平均运行时间和标准偏差作为误差。作为回报,我的time_analysis()函数将runtime()针对不同的max_iter(最大迭代)运行不同的函数,就像[10, 1E2, 1E3, 1E4, 1E5, 1E6, 1E7]这样我可以根据运行时间绘制最大迭代。然而,整个方法看起来相当麻烦和不优雅,因为我time_analysis()需要我反复计算时间和错误并将其附加到列表中。有没有更好的计时方法?(而且我的runtime()还不行,哈哈,因为我的algorithm论点似乎没有做点什么callable

1个回答

欢迎来到 SE SciComp。首先,我建议使用 Jupyter,这样您就可以访问 IPython 及其出色的计时魔法(请参阅%time 和 %timeit 魔法函数)。这些魔法考虑到您需要多次运行代码才能获得可靠的测量结果。如果您需要更深入的比较,您还需要考虑内存分配和缓存,您可以使用%prun 魔法

其次,比较向量化函数(在你的例子中是一个 NumPy 调用,它使用 C 作为后端)和你还执行 I/O 的例程(即在屏幕上打印你的进度条)之间的时间有点不公平。我可以理解 vanilla 非矢量化例程和 NumPy 例程之间的比较,但您不应该通过在屏幕上打印来惩罚 vanilla 例程。你把葡萄比作苹果...

第三,尽量避免例程中的 if-then-else 子句。为您想要计时/比较的每种情况(矢量化与非矢量化)编写单独的例程。

第四,在您的非向量化算法中,您在每次迭代中将一个值附加到一个列表中。既然你使用 NumPy,为什么不预先分配一个 size 的向量max_iter呢?

请参阅下面的代码(注意它必须在 IPython 环境中运行,即 Jupyter 笔记本或 Qtconsole 或普通 IPython 控制台)。这是你想要实现的愿望吗?

import numpy as np

def analytical_sine(max_iter):
    analytical_hist = np.zeros(max_iter)
    for i in range(max_iter):
        uni_dist = np.random.uniform(0, 1)
        analytical_hist[i] = np.arccos(1 - 2*uni_dist)
    return analytical_hist

def analytical_sine_vec(max_iter):
    return np.arccos(1 - 2*np.random.uniform(0, 1, max_iter))

print("Non-vectorized")
for i in range(10):
    print(f"Max_iter = {100*(i+1)}")
    %timeit res = analytical_sine(100*(i+1))
print("Vectorized")
for i in range(10):
    print(f"Max_iter = {100*(i+1)}")
    %timeit res = analytical_sine_vec(100*(i+1))