如何在 Octave 中对方法进行基准测试?
How to bechmark a method in Octave?
Matlab 有 timeit 方法,有助于比较一个实现与另一个实现的性能。我在八度音阶中找不到类似的东西。我编写了这个基准测试方法,运行一个函数 f N 次,然后 returns 花费的总时间。这是比较不同实现的合理方法,还是我遗漏了诸如“预热”之类的关键内容?
function elapsed_time_in_seconds = benchmark(f, N)
% benchmark runs the function 'f' N times and returns the elapsed time in seconds.
timeid = tic;
for i=1:N
output = f();
end
elapsed_time_in_seconds = toc(timeid);
end
MATLAB 的 timeit
执行以下操作(您可以阅读整个函数,它是一个 M 文件):
- 粗略估计
t_rough
调用函数 f
的时间。
- 使用估算值来确定
N
,使得 N*t_rough
约为 0.001 秒。
- 确定
M
使得M*N*t_rough
不超过15秒,但M
必须在3到11之间。
- 循环
M
次:
- 调用
f()
N
次并记录总时间。
- 确定
M
次的中位数,除以N
。
M
和N
这两个循环的目的如下:调用f()
N
次确保[=28=测量的时间]/toc
足够大以至于可靠,此循环避免尝试对太短以至于无法计时的内容进行计时。重复测量 M
次并保持尝试的中位数,以使测量对系统上发生的其他事情引起的延迟具有鲁棒性,这会人为地增加记录的时间。
函数减去通过其句柄调用函数的开销(通过对空函数的调用进行计时实验确定),以及tic
/toc
调用时间(也确定实验)。它并没有减去内层循环的开销,大概是因为在MATLAB中它被JIT优化了,它的开销可以忽略不计。
还有一些进一步的改进。确定 t_rough
的函数首先通过调用每个函数两次来预热 tic
和 toc
,然后它使用 while
循环来确保它调用 f()
for at至少 0.001 秒。但是在这个循环中,如果第一次迭代至少需要 3 s,那么就把这个时间作为粗略估计。如果第一次迭代花费的时间较少,则丢弃第一次计数(预热),然后使用所有后续调用的中位数作为时间的粗略估计。
使用正确数量的输出参数调用函数 f()
也付出了很多努力。
代码中有很多注释解释了所有这些步骤背后的原因,值得一读。
至少,我会按如下方式扩充您的基准函数:
function elapsed_time_in_seconds = benchmark(f, N, M)
% benchmark runs the function 'f' N*M times and returns the elapsed time in seconds.
tic; [~] = toc; tic; [~] = toc; % warmup
output = f(); % warmup
t = zeros(M, 1);
for k=1:M
timeid = tic;
for i=1:N
output = f();
end
t(k) = toc(timeid) / N;
end
elapsed_time_in_seconds = median(t);
end
如果使用函数直接比较各种备选方案,保持N
和M
不变,那么tic
、toc
、函数调用和循环的开销无关紧要。
此函数假定 f
有一个输出参数,但事实并非如此。您可以只调用 f()
而不是 output = f()
,这将适用于有或没有输出参数的函数。但是如果函数需要有一定数量的输出才能正常工作,或者触发你想要计时的计算,那么你必须调整函数以使用正确数量的输出参数调用它。
您可以想出一些启发式方法来从 N
中确定 M
,这将使使用此函数更容易一些。
Matlab 有 timeit 方法,有助于比较一个实现与另一个实现的性能。我在八度音阶中找不到类似的东西。我编写了这个基准测试方法,运行一个函数 f N 次,然后 returns 花费的总时间。这是比较不同实现的合理方法,还是我遗漏了诸如“预热”之类的关键内容?
function elapsed_time_in_seconds = benchmark(f, N)
% benchmark runs the function 'f' N times and returns the elapsed time in seconds.
timeid = tic;
for i=1:N
output = f();
end
elapsed_time_in_seconds = toc(timeid);
end
MATLAB 的 timeit
执行以下操作(您可以阅读整个函数,它是一个 M 文件):
- 粗略估计
t_rough
调用函数f
的时间。 - 使用估算值来确定
N
,使得N*t_rough
约为 0.001 秒。 - 确定
M
使得M*N*t_rough
不超过15秒,但M
必须在3到11之间。 - 循环
M
次: - 调用
f()
N
次并记录总时间。 - 确定
M
次的中位数,除以N
。
M
和N
这两个循环的目的如下:调用f()
N
次确保[=28=测量的时间]/toc
足够大以至于可靠,此循环避免尝试对太短以至于无法计时的内容进行计时。重复测量 M
次并保持尝试的中位数,以使测量对系统上发生的其他事情引起的延迟具有鲁棒性,这会人为地增加记录的时间。
函数减去通过其句柄调用函数的开销(通过对空函数的调用进行计时实验确定),以及tic
/toc
调用时间(也确定实验)。它并没有减去内层循环的开销,大概是因为在MATLAB中它被JIT优化了,它的开销可以忽略不计。
还有一些进一步的改进。确定 t_rough
的函数首先通过调用每个函数两次来预热 tic
和 toc
,然后它使用 while
循环来确保它调用 f()
for at至少 0.001 秒。但是在这个循环中,如果第一次迭代至少需要 3 s,那么就把这个时间作为粗略估计。如果第一次迭代花费的时间较少,则丢弃第一次计数(预热),然后使用所有后续调用的中位数作为时间的粗略估计。
使用正确数量的输出参数调用函数 f()
也付出了很多努力。
代码中有很多注释解释了所有这些步骤背后的原因,值得一读。
至少,我会按如下方式扩充您的基准函数:
function elapsed_time_in_seconds = benchmark(f, N, M)
% benchmark runs the function 'f' N*M times and returns the elapsed time in seconds.
tic; [~] = toc; tic; [~] = toc; % warmup
output = f(); % warmup
t = zeros(M, 1);
for k=1:M
timeid = tic;
for i=1:N
output = f();
end
t(k) = toc(timeid) / N;
end
elapsed_time_in_seconds = median(t);
end
如果使用函数直接比较各种备选方案,保持N
和M
不变,那么tic
、toc
、函数调用和循环的开销无关紧要。
此函数假定 f
有一个输出参数,但事实并非如此。您可以只调用 f()
而不是 output = f()
,这将适用于有或没有输出参数的函数。但是如果函数需要有一定数量的输出才能正常工作,或者触发你想要计时的计算,那么你必须调整函数以使用正确数量的输出参数调用它。
您可以想出一些启发式方法来从 N
中确定 M
,这将使使用此函数更容易一些。