MATLAB中的定时程序执行;奇怪的结果

时间:2010-09-07 03:26:40

标签: matlab

我有一个程序是我从教科书中复制过来的,当使用未初始化的,初始化的数组和向量计算相同的东西时,程序执行运行时间的差异会增加。

然而,虽然程序运行有点像预期的那样,如果偶尔运行几次,它会发出疯狂的结果。请参阅下面的程序和 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在哪里“找到”那个号码?

4 个答案:

答案 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位整数作为时间测量。请考虑以下可能的情况,其中两个脚本在不同的线程上同时运行:

  1. 第一个线程调用TIC,将全局变量初始化为开始时间度量(即当前时间)。
  2. 然后第一个线程调用TOC,TOC函数可能立即执行的操作是获取当前时间度量。
  3. 第二个线程调用TIC,将全局开始时间度量重置为当前时间,晚于第一个线程的TOC函数测量的时间。
  4. 第一个线程的TOC函数访问全局开始时间度量,以获得它与之前采取的度量之间的差异。除了时间度量是无符号整数之外, 这个差值会产生负数。这导致整数溢出,给出时间差的巨大正数。
  5. 那么,你怎么能避免这个问题呢?将脚本更改为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这样的高级语言的高分辨率计时有点问题。定时工作负荷可降至几百微秒,使其受到机器状态下其他瞬态条件的噪声影响。