Подтвердить что ты не робот

Точность Matlab tic toc

Я измеряю некоторый код в цикле

fps = zeros(1, 100);
for i=1:100

    t = tic
    I = fetch_image_from_source(); % function to get image
    fps(i) = 1./ toc(t);

end
plot(fps);

И я получаю средние 50 кадров в секунду.

Затем я хотел бы добавить imshow() в свой код. Я понимаю, что imshow работает очень медленно, но я не буду включать imshow внутри tic-toc команд:

fps = zeros(1, 100);
figure;
for i=1:100

    t = tic
    I = fetch_image_from_source(); % function to get image
    fps(i) = 1./ toc(t);

    imshow(I); drawnow;

end
plot(fps);

И я получаю fps примерно на 20% -30% медленнее. Почему это происходит? Поскольку imshow() находится вне tic-toc

4b9b3361

Ответ 1

Вот matlab doc о времени в целом и о том, как прошло время и в настоящее время измеряется в Matlab. Мы можем прочитать, что "tic и toc [предлагает] высочайшую точность и наиболее предсказуемое поведение". Я думаю, что это действительная инструкция.

Снижение производительности, наблюдаемое здесь, происходит не из-за плохой меры прошедшего времени, а также не связано с использованием функций imshow или drawnow. Я буду утверждать, что это связано с системой кэширования.

На приведенном ниже рисунке показаны результаты четырех тестов, каждый из которых имеет свою собственную базовую меру tic/toc (нарисованную синим цветом) на 100 итераций. Зеленая линия показывает производительность в разных условиях:

(1)    for ii=1:100
         t = tic;                %single tic/toc
         fps(ii,2) = 1./toc(t); 
         rand(1000);             %extra function outside tic/toc
       end

Как сообщалось в вашем вопросе, мы можем наблюдать более медленный кадр в секунду (FPS, я бы сказал, 30%), несмотря на то, что rand находится за пределами блока tic/toc. Дополнительная функция может быть любого типа (plot, surf, imshow, sum), вы всегда будете наблюдать за снижением производительности.

(2)    for ii=1:100
         t = tic;                %first tic/toc
         fps(ii,2) = 1./toc(t); 
         t = tic;                %second tic/toc
         fps(ii,2) = 1./toc(t);
         rand(1000);             %extra function outside tic/toc
       end

Во втором подзаголовке блок tic/toc повторяется дважды. Поэтому измерение fps выполняется два раза, и сохраняется только вторая мера. Мы видим, что падение производительности больше не существует - точно так же, как первый вызов tic/toc подготовил второй (разминка). Я интерпретирую это в терминах кеша: инструкции и/или данные выполняются, а затем хранятся в памяти низкого уровня - второй вызов выполняется быстрее.

(3)    for ii=1:100
         t = tic;                     %first tic/toc
         fps(ii,2) = 1./toc(t);
         for ij = 1:10000             %10,000 extra tic/toc
           tic;
           tmp = toc;
         end
       end

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

(4)    for ii=1:100               %first tic/toc block
         t = tic;   
         fps(ii,1) = 1./toc(t);
       end
       for ii=1:100               %second tic/toc block
         t = tic;   
         fps(ii,2) = 1./toc(t);
       end

Наконец, четвертый подзаголовок показывает два последовательных блока вызовов tic/toc. Мы видим, что второй работает лучше первого (эффект разогрева).

Общий шаблон, показанный здесь, не связан с imshow, не зависит от JIT от accel, но зависит только от последовательных вызовов определенной функции. Я интерпретирую это с точки зрения кеша, но мне не хватает формальных доказательств.

Вот графики

enter image description here

и код

%% EXTRA FUNCTION (single call)
fps = zeros(2, 100);

% first case: 100 tic/toc
for ii=1:100
    t = tic;   
    fps(ii,1) = 1./toc(t);
end

%second case: 100 tic/toc + additional function
for ii=1:100

    t = tic;   
    fps(ii,2) = 1./toc(t);

    % graph or scalar functions (uncomment to test)
    %drawnow;
    %plot(1:10)
    rand(1000);          
    %ones(1000, 1000);
    %sum(1:1000000);
    %diff(1:1000000);
end


h = figure('Color','w','Position',[10 10 600 800]);

subplot(4,1,1);
plot(fps); legend({'tic/toc only','extra function'});
ylabel('FPS');
title('extra function, single call','FontSize',14);
set(gca,'FontSize',14, 'YLim', [0 3.5e5]);

%% EXTRA FUNCTION (double call)
fps = zeros(2, 100);

% first case: 100 tic/toc
for ii=1:100
    t = tic;   
    fps(ii,1) = 1./toc(t);
end

%second case: 100 tic/toc + additional function (except tic/toc)
for ii=1:100

    %first call
    t = tic;   
    fps(ii,2) = 1./toc(t);

    %second call (identical to first)
    t = tic;   
    fps(ii,2) = 1./toc(t);

    rand(1000);
end

subplot(4,1,2);
plot(fps); legend({'tic/toc only','extra function'});
ylabel('FPS');
title('extra function, double call','FontSize',14);
set(gca,'FontSize',14, 'YLim', [0 3.5e5]);


%% EXTRA FUNCTION (double call)
fps = zeros(2, 100);

% first case: 100 tic/toc
for ii=1:100
    t = tic;   
    fps(ii,1) = 1./toc(t);
end

%second case: 100 tic/toc + 10000 tic/toc
for ii=1:100

    t = tic;   
    fps(ii,2) = 1./toc(t);

    for ij = 1:10000
        tic;
        tmp = toc;
    end

end


subplot(4,1,3);
plot(fps); legend({'tic/toc','extra tic/toc'});
ylabel('FPS');
title('Identical function calls','FontSize',14);
set(gca,'FontSize',14, 'YLim', [0 3.5e5]);


%% TIC/TOC call twice
fps = zeros(2, 100);

% first case: 100 tic/toc
for ii=1:100
    t = tic;   
    fps(ii,1) = 1./toc(t);
end

for ii=1:100
    t = tic;   
    fps(ii,2) = 1./toc(t);
end

subplot(4,1,4);
plot(fps); legend({'tic/toc (1)','tic/toc (2)'});
ylabel('FPS');
title('tic/toc twice','FontSize',14);
set(gca,'FontSize',14, 'YLim', [0 3.5e5]);

Ответ 2

Это может быть связано с возможностью многопоточности вашего процессора.

Количество вычислительных потоков, используемых MATLAB, основано на значении maxNumCompThreads. Если вы установите значение 1, то оба случая должны теоретически дать один и тот же fps.

Вы можете достичь этого как:

LASTN = maxNumCompThreads(N);

Здесь N должно быть 1, а LASTN предоставит вам предыдущее максимальное количество вычислительных потоков, которое может быть полезно позже, если вы хотите reset предпочтение.