2010-09-07 40 views
10

我有一个程序,我从教科书中复制,哪些时间在计算与未初始化,初始化的数组和矢量相同的事情时程序执行运行时的差异。在MATLAB中执行定时程序;奇怪的结果

但是,虽然程序运行有点像预期的那样,如果每隔一段时间运行几次,它会发出一个疯狂的结果。请参阅下面的程序和疯狂结果的例子。

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

可能是当存储在toc中的时钟值翻转时? – gary 2010-09-07 03:29:44

+0

您使用的是什么MATLAB版本? – gnovice 2010-09-07 03:36:29

+0

@gnovice - 7.10.0.499(2010a) – Rook 2010-09-07 03:38:20

回答

1

至少有两个可能的错误来源。你可以尝试通过仅仅查看计算值而不用格式化来区分'tic/toc'和'fprintf'。

我不明白'toc'的大括号,但他们不应该有任何伤害。

+0

是的,averageX确实得到了很大的价值。所以,这个问题不在fprintf函数中。大括号只是我自己尝试过的东西,之后我忘了删除它们。他们没有伤害。 – Rook 2010-09-07 12:22:24

+0

如果增加迭代次数(maxcount),会发生什么情况。你在做什么平台? – zellus 2010-09-07 14:58:31

+0

@Rook:你也可以看看下面的文章。然后结尾的帖子以及评论都很有趣。 http://undocumentedmatlab.com/blog/tic-toc-undocumented-option/ – zellus 2010-09-07 15:35:12

3

这确实很疯狂。不知道是什么原因造成的,并且无法在我自己的Matlab R2010a上复制几次运行,按名称或通过F5调用。

这是一个调试它的想法。

在脚本或函数中使用tic/toc时,使用捕获输出的“tstart = tic”表单。这可以安全地使用嵌套的tic/toc调用(例如,在内部调用函数),并让您保持多个开始时间和耗用时间,并以编程方式检查它们。

t0 = tic; 
% ... do some work ... 
te = toc(t0); % "te" for "time elapsed" 

您可以使用不同的“t0_label”后缀为每个TIC和TOC的回报,或将它们存储在一个矢量,所以你保留它们,直到你的脚本结束。

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/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 

基于MATLAB R2010b中(预发布),其中有Int64的数学,你可以通过盘车拉坯抽动值的参考是“未来”重现了类似的荒谬TOC结果。看起来像一个int rollover效果,正如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()在破碎的系统上。

UPDATE 2012-03-28:我已经在野外见过一段时间了,很可能是由于与CPU的高分辨率性能计时器和速度缩放的交互,以及(在Windows上)QueryPerformanceCounter,如此处所述:http://support.microsoft.com/kb/895980/。这不是tic/toc中的错误,问题在于tic/toc调用的操作系统功能。设置启动参数可以解决它。

+0

虽然仍然不确定原因,但似乎增加...呃,如果你可以阅读我的评论zellus不要在这里重复。我现在也试试这个。 – Rook 2010-09-07 15:49:05

+0

很好的解释,帮助我解决我的问题。 – Yasin 2015-04-21 20:29:24

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函数访问全局开始时间度量以获取它与之前采取的度量之间的差异。这个差异导致负数,除了时间度量是无符号整数。这会导致整数溢出,给出时间差异的巨大正数。

那么,你会如何避免这个问题呢?将脚本更改为像Amro这样的功能可能是最好的选择,因为这似乎规避了问题使工作空间变得混乱。另一种变通,你可以尝试是计算的最大线程数设置为一个:

maxNumCompThreads(1); 

这应该让你的脚本的多个副本在主工作区中同时运行。

+0

我考虑过脚本和函数的影响,以及运行命令行上运行的东西时运行保存的m文件时JIT的影响。仍然我无法重现这个问题(我运行与OP相同的MATLAB版本和操作系统)..这可能是一个竞争条件错误确实 – Amro 2010-09-07 19:34:45

+0

@Amro:不幸的是,我没有相同的MATLAB版本作为OP,所以我不能尝试重现它。我*可以*说我无法在R2009a中重现它。没有R2010a,以上是我最好的猜测。 ;) – gnovice 2010-09-07 19:44:34

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这样的高级语言的高分辨率时序有点问题。定时工作负载降至几百微秒会使其受到机器状态下其他瞬态条件的干扰。