Выполнение программы синхронизации в 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 "нашел" этот номер?

Ответ 1

Это действительно безумие. Не знаю, что может вызвать это, и не смог воспроизвести на моей собственной копии Matlab R2010a несколько запусков, вызванных по имени или через F5.

Вот идея его отладки.

При использовании tic/toc внутри script или функции используйте форму tstart = tic, которая фиксирует вывод. Это позволяет безопасно использовать вложенные вызовы tic/toc (например, внутри вызываемых функций) и позволяет вам удерживать несколько старт и прошедшее время и анализировать их программно.

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

Вы можете использовать разные суффиксы "t0_label" для каждого из возвратов tic и toc или хранить их в векторе, чтобы сохранить их до конца вашего script.

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);

Попросите script войти в отладчик, когда он найдет одно из больших значений.

if any([te_uninit te_prealloc te_vector] > 5)
    keyboard
end

Затем вы можете проверить рабочее пространство и возвращаемые значения из tic, что может дать некоторые подсказки.


EDIT: вы также можете попробовать проверить tic() самостоятельно, чтобы увидеть, есть ли что-то странное с вашими системными часами или любым тиком/током. Возвращаемое значение 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 (preerelease), который имеет математику int64, вы можете воспроизвести подобный нелепый результат toc путем смещения ссылочного значения tic как "в будущем". Похоже на эффект опрокидывания, как предположил гэри comtois.

>> t0 = tic; toc(t0+999999)
Elapsed time is 6148914691.236258 seconds.

Это говорит о том, что если в таймере был некоторый джиттер, который использовал toc, вы можете опрокинуться, если это произойдет, когда вы выполняете очень короткие операции. (Я полагаю, что toc() внутренне делает что-то вроде tic(), чтобы получить значение для сравнения ввода.) Увеличение количества итераций может привести к тому, что эффект исчезнет, ​​потому что небольшое количество дрожания часов будет менее значительным как часть более длительного tic/toc. Также объясните, почему вы не видите этого в своем непересекаемом тесте, который занимает больше времени.


UPDATE: я смог воспроизвести это поведение. Я работал над некоторым несвязанным кодом и обнаружил, что на одном конкретном настольном компьютере с моделью ЦП, которую мы раньше не использовали, ядро ​​Core 2 Q8400 2.66 ГГц, 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 будет регулярно сообщать об истекшем времени для операций, особенно для низких задач использования ЦП.

>> 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, которые используют тест я = 1: 50000, чтобы определить, когда tic отключен на текущей машине, обычно используйте tic/toc и предупреждайте их и возвращайтесь к использованию etime() на сломанных системах.

UPDATE 2012-03-28: я уже давно это видел в дикой природе, и это очень вероятно из-за взаимодействия с таймером производительности с высоким разрешением процессора и масштабированием скорости и (в Windows) QueryPerformanceCounter, так как описанный здесь: http://support.microsoft.com/kb/895980/. Это не ошибка в tic/toc, проблема в функциях ОС, которые вызывает tic/toc. Установка параметра загрузки может обойти его.

Ответ 2

Вот моя теория о том, что может произойти, на основе этих двух частей данных, которые я нашел:

Объединяя эти две вещи, мне интересно, могут ли несколько версий вашего script, которые работают в рабочей области, одновременно сбрасывать глобальные переменные, используемые TIC/TOC и вкручивают друг друга вверх. Возможно, это не проблема при преобразовании вашего script в функцию Amro, так как это будет разделять рабочие пространства, в которых работают две программы (т.е. Оба они не будут работать в основном рабочем пространстве).

Это также может объяснить чрезвычайно большие числа, которые вы получаете. Как указывали Гэри и Эндрю, эти числа, по-видимому, связаны с эффектом целочисленного опрокидывания (т.е. integer overflow), в результате чего начальный время (от TIC) больше времени окончания (от TOC). Это приведет к огромному числу, которое по-прежнему положительно, поскольку TIC/TOC внутренне используют неподписанные 64-битные целые числа в качестве временных мер. Рассмотрим следующий возможный сценарий, в котором два сценария работают одновременно с разными потоками:

  • Первый поток вызывает TIC, инициализируя глобальную переменную до значения времени начала (т.е. текущего времени).
  • Первый поток затем вызывает TOC, и немедленное действие, которое может сделать функция TOC, - это получить текущую временную меру.
  • Второй поток вызывает TIC, сбросив значение глобального времени запуска на текущее время, которое позже времени, только что измеренного функцией TOC для первого потока.
  • Функция TOC для первого потока обращается к измерению глобального времени начала, чтобы получить разницу между ним и измерением, которое оно ранее принимало. Это различие приведет к отрицательному числу, за исключением того, что временные меры представляют собой целые числа без знака. Это приводит к целочисленному переполнению, что дает огромное положительное число для разницы во времени.

Итак, как вы могли избежать этой проблемы? Изменение ваших сценариев на такие функции, как Amro, вероятно, является лучшим выбором, поскольку это, похоже, обходит проблему и препятствует тому, чтобы рабочее пространство стало загроможденным. Альтернативный подход, который вы можете попробовать, - установить максимальное число вычислительных потоков на один:

maxNumCompThreads(1);

Это должно привести к тому, что несколько копий вашего script будут запущены одновременно в основной рабочей области.

Ответ 3

Есть как минимум два возможных источника ошибок. Можете ли вы попытаться различать "tic/toc" и "fprintf", просто глядя на вычисленные значения без их форматирования.

Я не понимаю брекеты вокруг "toc", но они не должны наносить никакого вреда.

Ответ 4

Вот гипотеза, которую можно проверить. Matlab tic()/toc() должен использовать некоторый таймер с высоким разрешением. В Windows, поскольку их возвращаемое значение выглядит так же, как и тактовые циклы, я думаю, что они используют вызов Win32 QueryPerformanceCounter() или, возможно, что-то еще нажимают на счетчик времени метки 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 к одному процессору. (На вкладке "Процессы" щелкните правой кнопкой мыши MATLAB.exe, "Установить аффинность...", отмените проверку всех, кроме CPU 0.) Если сумасшедшее время уходит, когда настроено аффинность, похоже, что вы нашли причину.

Несмотря на это, обходной путь похож на просто увеличение maxcount, поэтому вы выбираете более длинные фрагменты работы, и шум, который вы, по-видимому, получаете в tic()/toc(), мал по сравнению с измеренным значением. (Вы не хотите, чтобы нужно было гадать с близостью процессора, Matlab должен быть легко запущен.) Если есть проблема, вызывающая переполнение int, другие небольшие положительные числа тоже немного подозрительны. Кроме того, синхронизация hi-res на языке высокого уровня, таком как Matlab, немного проблематична. Сроки работы до нескольких сотен микросекунд подвергают их шуму от других переходных условий в вашем состоянии машины.