我有一个程序是我从教科书中复制过来的,当使用未初始化的,初始化的数组和向量计算相同的东西时,程序执行运行时间的差异会增加。
然而,虽然程序运行有点像预期的那样,如果偶尔运行几次,它会发出疯狂的结果。请参阅下面的程序和 crazy 结果的示例。
clear all; clc;
% Purpose:
% This program calculates the time required to calculate the squares of
% all integers from 1 to 10000 in three different ways:
% 1. using a for loop with an uninitialized output array
% 2. Using a for loop with a pre-allocated output array
% 3. Using vectors
% PERFORM CALCULATION WITH AN UNINITIALIZED ARRAY
% (done only once because it is so slow)
maxcount = 1;
tic;
for jj = 1:maxcount
clear square
for ii = 1:10000
square(ii) = ii^2;
end
end
average1 = (toc)/maxcount;
% PERFORM CALCULATION WITH A PRE-ALLOCATED ARRAY
% (averaged over 10 loops)
maxcount = 10;
tic;
for jj = 1:maxcount
clear square
square = zeros(1,10000);
for ii = 1:10000
square(ii) = ii^2;
end
end
average2 = (toc)/maxcount;
% PERFORM CALCULATION WITH VECTORS
% (averaged over 100 executions)
maxcount = 100;
tic;
for jj = 1:maxcount
clear square
ii = 1:10000;
square = ii.^2;
end
average3 = (toc)/maxcount;
% Display results
fprintf('Loop / uninitialized array = %8.6f\n', average1)
fprintf('Loop / initialized array = %8.6f\n', average2)
fprintf('Vectorized = %8.6f\n', average3)
结果 - 正常:
Loop / uninitialized array = 0.195286
Loop / initialized array = 0.000339
Vectorized = 0.000079
结果 - 疯狂:
Loop / uninitialized array = 0.203350
Loop / initialized array = 973258065.680879
Vectorized = 0.000102
为什么会发生这种情况? (有时疯狂的数字是矢量化的,有时是循环初始化的)
MATLAB在哪里“找到”那个号码?
答案 0 :(得分:3)
这确实很疯狂。不知道是什么原因引起的,并且无法通过我自己的Matlab R2010a副本在多次运行中重现,通过名称或F5调用。
这是调试它的想法。
在脚本或函数中使用tic / toc时,请使用捕获输出的“tstart = tic”表单。这样可以安全地使用嵌套的tic / toc调用(例如内部调用函数),并让您保持多个开始和已用时间并以编程方式检查它们。
t0 = tic;
% ... do some work ...
te = toc(t0); % "te" for "time elapsed"
您可以为每个tic和toc返回使用不同的“t0_label”后缀,或将它们存储在向量中,因此您可以保留它们直到脚本结束。
t0_uninit = tic;
% ... do the uninitialized-array test ...
te_uninit = toc(t0_uninit);
t0_prealloc = tic;
% ... test the preallocated array ...
te_prealloc = toc(t0_prealloc);
当脚本找到一个较大的值时,让脚本插入调试器。
if any([te_uninit te_prealloc te_vector] > 5)
keyboard
end
然后你可以检查工作区和tic的返回值,这可能会提供一些线索。
编辑:您也可以尝试自己测试tic(),看看您的系统时钟是否有些奇怪,或者调用tic / toc。 tic()的返回值看起来像某种本机时间戳。尝试连续多次调用它并比较后续值。如果它倒退,那将是令人惊讶的。
function test_tic
t0 = tic;
for i = 1:1000000
t1 = tic;
if t1 <= t0
fprintf('tic went backwards: %s to %s\n', num2str(t0), num2str(t1));
end
t0 = t1;
end
在具有int64数学运算的Matlab R2010b(预发行版)中,您可以通过将引用tic值变为“将来”来重现类似的荒谬toc结果。看起来像一个int翻转效果,如gary comtois所建议的那样。
>> t0 = tic; toc(t0+999999)
Elapsed time is 6148914691.236258 seconds.
这表明如果toc正在使用的计时器中存在一些抖动,如果在执行非常短的操作时发生翻转,则可能会发生翻转。 (我假设toc()在内部执行类似tic()的操作来获取一个值来比较输入。)增加迭代次数可能会使效果消失,因为较少的时钟抖动会不那么重要tic / toc期间。还可以解释为什么你在非预分配的测试中没有看到这个,这需要更长的时间。
更新:我能够重现此行为。我正在研究一些不相关的代码,发现在一个我们以前没有使用的CPU型号的特定桌面上,Core 2 Q8400 2.66GHz四核,tic给出了不准确的结果。看起来像tic / toc中的系统相关错误。
在这台特定的机器上,tic / toc会定期报告像你这样的高价值。
>> for i = 1:50000; t0 = tic; te = toc(t0); if te > 1; fprintf('elapsed: %.9f\n', te); end; end
elapsed: 6934787980.471930500
elapsed: 6934787980.471931500
elapsed: 6934787980.471899000
>> for i = 1:50000; t0 = tic; te = toc(t0); if te > 1; fprintf('elapsed: %.9f\n', te); end; end
>> for i = 1:50000; t0 = tic; te = toc(t0); if te > 1; fprintf('elapsed: %.9f\n', te); end; end
elapsed: 6934787980.471928600
elapsed: 6934787980.471913300
>>
它过去了。在这台机器上,tic / toc将定期低估操作所用的时间,特别是对于CPU使用率低的任务。
>> t0 = tic; c0 = clock; pause(4); toc(t0); fprintf('Wall time is %.6f seconds.\n', etime(clock, c0));
Elapsed time is 0.183467 seconds.
Wall time is 4.000000 seconds.
所以看起来这是tic / toc中与特定CPU模型(或系统配置特有的其他内容)相关的错误。我已经向MathWorks报告了这个错误。
这意味着tic / toc可能会给你不准确的结果,即使它不会产生那些疯狂的大数字。作为一种解决方法,在这台机器上,使用etime()代替,并且只花费更长时间的工作来补偿etime的较低分辨率。您可以将它包装在您自己的tick / tock函数中,这些函数使用for i = 1:50000测试来检测当前机器上的tic何时被破坏,正常使用tic / toc,并让它们发出警告并回退到使用etime()在破碎的系统上。
更新2012-03-28:我已经在野外看了一段时间了,这很可能是由于与CPU的高分辨率性能计时器和速度缩放的交互,以及(在Windows上)QueryPerformanceCounter,这里描述:http://support.microsoft.com/kb/895980/。它不是tic / toc中的错误,问题出在tic / toc调用的OS功能中。设置引导参数可以解决它。
答案 1 :(得分:2)
根据我发现的这两个数据,这是我关于可能发生的事情的理论:
有一个函数maxNumCompThreads
可以控制MATLAB执行任务所使用的最大计算线程数。引用文档:
默认情况下,MATLAB使用 多线程的能力 正在运行的计算机。
这让我觉得你的脚本可能同时运行多个副本。
This newsgroup thread讨论旧版MATLAB(R14)中的一个错误“以MATLAB加速具有全局结构变量的M代码的方式”,看来TIC/TOC函数可能使用。解决方案是使用未记录的FEATURE函数禁用加速器:
feature accel off
将这两件事放在一起,我想知道在工作区中运行的脚本的多个版本是否可以同时重置TIC/TOC函数使用的全局变量并相互拧紧。在将您的脚本转换为Amro所做的功能时,这可能不是问题,因为这会分离运行这两个程序的工作区(即它们不会在主工作区中运行)。
这也可以解释你得到的数字非常大。正如加里和安德鲁所指出的那样,这些数字似乎是由于整数翻转效应(即integer overflow),其中起始时间(来自TIC)大于结束时间(来自TOC)。这将导致数量仍然很大,因为TIC / TOC在内部使用无符号 64位整数作为时间测量。请考虑以下可能的情况,其中两个脚本在不同的线程上同时运行:
那么,你怎么能避免这个问题呢?将脚本更改为Amro等功能可能是最佳选择,因为这似乎可以避免问题和使工作空间变得混乱。您可以尝试的另一种解决方法是将最大计算线程数设置为1:
maxNumCompThreads(1);
这应该使您的脚本的多个副本不会在主工作区中同时运行。
答案 2 :(得分:1)
至少有两种可能的错误来源。您可以尝试通过查看计算值而不格式化它们来区分'tic / toc'和'fprintf'。
我不理解'toc'周围的牙套,但它们不应该造成任何伤害。
答案 3 :(得分:1)
这是一个可以检验的假设。 Matlab的tic()/ toc()必须使用一些高分辨率计时器。在Windows上,因为它们的返回值看起来像时钟周期,我认为它们正在使用Win32 QueryPerformanceCounter()调用,或者可能还有其他东西命中CPU的RDTSC时间戳计数器。这些显然在某些多处理器系统上存在故障,在链接文章中提到。也许你的机器就是其中之一,如果过程调度程序将Matlab进程从核心移动到核心,那么会得到不同的结果。
http://msdn.microsoft.com/en-us/library/ms644904(VS.85).aspx
http://www.virtualdub.org/blog/pivot/entry.php?id=106
这将取决于硬件和系统配置,这可以解释为什么其他海报无法重现它。
尝试使用Windows任务管理器将Matlab.exe进程的亲缘关系设置为单个CPU。 (在进程选项卡上,右键单击MATLAB.exe,“设置关联...”,取消检查除CPU 0之外的所有内容。)如果在设置关联时疯狂的时间消失,看起来就像找到了原因。
无论如何,解决方法看起来只是增加maxcount,这样你就可以计算更长的工作时间,并且与测量值相比,你在tic()/ toc()中显然得到的噪音很小。 (您不希望不得不使用CPU亲和力; Matlab应该很容易运行。)如果存在导致int溢出的问题,其他小的正数也有点怀疑。此外,像Matlab这样的高级语言的高分辨率计时有点问题。定时工作负荷可降至几百微秒,使其受到机器状态下其他瞬态条件的噪声影响。