Поиск…


замечания

Определение узких мест производительности с помощью Profiler

MATLAB Profiler - это инструмент для профилирования программного кода MATLAB. Используя Profiler, можно получить визуальное представление как времени выполнения, так и потребления памяти.

Запуск Profiler можно выполнить двумя способами:

  • Нажатие кнопки «Выполнить и время» в графическом интерфейсе MATLAB при открытии некоторого файла .m в редакторе (добавлено в R2012b ).

    Кнопка в toolstrip

  • Программно, используя:

    profile on
    <some code we want to test>
    profile off
    

Ниже приведен пример кода примера и результат его профилирования:

function docTest

for ind1 = 1:100
  [~] = var(...
            sum(...
                randn(1000)));
end

spy

Выход профилировщика

Из вышесказанного мы узнаем, что функция spy занимает около 25% от общего времени выполнения. В случае «реального кода» функция, которая занимает такой большой процент времени выполнения, будет хорошим кандидатом на оптимизацию, в отличие от функций, аналогичных var и cla , оптимизация которых следует избегать.

Кроме того, можно щелкнуть записи в столбце « Имя функции», чтобы просмотреть подробную разбивку времени выполнения для этой записи. Вот пример щелчка spy :

Внутреннее время «шпиона»


Также возможно профилировать потребление памяти, выполняя profile('-memory') перед запуском Profiler.

введите описание изображения здесь

Сравнение времени выполнения нескольких функций

Широко используемая комбинация tic и toc может дать приблизительное представление о времени выполнения функции или фрагментов кода.

Для сравнения нескольких функций он не должен использоваться. Зачем? Почти невозможно обеспечить равные условия для всех фрагментов кода для сравнения в скрипте с использованием вышеприведенного решения. Возможно, функции имеют одно и то же функциональное пространство и общие переменные, поэтому более поздние функции и фрагменты кода уже используют ранее инициализированные переменные и функции. Также нет никакой информации о том, будет ли компилятор JIT обрабатывать эти впоследствии называемые фрагменты одинаково.


timeit функция для эталонных тестов - timeit . Следующий пример иллюстрирует его использование.

Есть массив A и матрица B Следует определить, какая строка B наиболее похожа на A , подсчитывая количество разных элементов.

function t = bench()
    A = [0 1 1 1 0 0];
    B = perms(A);

    % functions to compare
    fcns = {
        @() compare1(A,B);
        @() compare2(A,B);
        @() compare3(A,B);
        @() compare4(A,B);
    };

    % timeit
    t = cellfun(@timeit, fcns);
end

function Z = compare1(A,B)  
    Z = sum(  bsxfun(@eq,  A,B) , 2);
end
function Z = compare2(A,B)  
    Z = sum(bsxfun(@xor, A, B),2);
end
function Z = compare3(A,B)  
    A = logical(A);
    Z = sum(B(:,~A),2) + sum(~B(:,A),2);
end
function Z = compare4(A,B)  
     Z = pdist2( A, B, 'hamming', 'Smallest', 1 );
end

Этот метод тестирования был впервые замечен в этом ответе .

Это нормально быть «единственным»!

Обзор:

Тип данных по умолчанию для числовых массивов в MATLAB является double . double - это число чисел с плавающей запятой , и этот формат занимает 8 байтов (или 64 бит) за каждое значение. В некоторых случаях, когда, например, работа с целыми числами или когда числовая неустойчивость не является непосредственной проблемой, такая высокая глубина бит может не потребоваться. По этой причине рекомендуется учитывать преимущества single точности (или других подходящих типов ):

  • Более быстрое время выполнения (особенно заметно на графических процессорах).
  • Половина потребления памяти: может преуспеть, если double сбой из-за ошибки из-за памяти; более компактный при хранении в виде файлов.

Преобразование переменной из любого поддерживаемого типа данных в single выполняется с помощью:

sing_var = single(var);

Некоторые обычно используемые функции (такие как: zeros , eye , ones и т. Д. ), Которые по умолчанию выдают double значения, позволяют указать тип / класс вывода.

Преобразование переменных в сценарий в нестандартную точность / тип / класс:

По состоянию на июль 2016 года не существует документального способа изменения типа данных MATLAB по умолчанию из double .

В MATLAB новые переменные обычно имитируют типы данных переменных, используемых при их создании. Чтобы проиллюстрировать это, рассмотрим следующий пример:

A = magic(3);
B = diag(A);
C = 20*B;
>> whos C
  Name      Size            Bytes  Class     Attributes
  C         3x1                24  double 
A = single(magic(3)); % A is converted to "single"
B = diag(A);
C = B*double(20);     % The stricter type, which in this case is "single", prevails
D = single(size(C));  % It is generally advised to cast to the desired type explicitly.
>> whos C
  Name      Size            Bytes  Class     Attributes
  C         3x1                12  single  

Таким образом, может показаться достаточным для перевода / преобразования нескольких начальных переменных, чтобы изменение пронизывало всюду по коду - однако это не рекомендуется (см. Ниже « Оговорки и ошибки» ).

Предостережения и ошибки:

  1. Повторные конверсии не рекомендуется из-за введения числового шума (при литье из single в double ) или потери информации (при кастинге от double до single или между определенными целыми типами ), например:

    double(single(1.2)) == double(1.2)   
    ans =
         0
    

    Это можно немного смягчить, используя typecast . См. Также Будьте в курсе неточности с плавающей запятой .

  2. Опираясь исключительно на неявное типирование данных (то есть, что MATLAB догадывается, какой тип вывода вычисления должен быть) не рекомендуется из-за нескольких нежелательных эффектов, которые могут возникнуть:

    • Потеря информации : когда ожидается double результат, но небрежное сочетание single и double операндов дает single точность.

    • Неожиданно высокая потребляемая память : когда ожидается single результат, но небрежное вычисление приводит к double выходу.

    • Ненужные накладные расходы при работе с графическими процессорами : при смешивании типов gpuArray (т. gpuArray Переменных, хранящихся в VRAM) с переменными gpuArray (то есть, которые обычно хранятся в ОЗУ) данные должны быть переданы так или иначе, прежде чем вычисление может быть выполнено. Эта операция требует времени и может быть очень заметна при повторных вычислениях.

    • Ошибки при смешивании типов с плавающей запятой с целыми типами : такие функции, как mtimes ( * ), не определены для смешанных входов целых чисел и типов с плавающей точкой - и будут ошибки. Функции типа times ( .* ) Вообще не определены для входов целых типов - и снова будут ошибки.

      >> ones(3,3,'int32')*ones(3,3,'int32')
      Error using  * 
      MTIMES is not fully supported for integer classes. At least one input must be scalar.
      
      >> ones(3,3,'int32').*ones(3,3,'double')
      Error using  .* 
      Integers can only be combined with integers of the same class, or scalar doubles.
      

    Для лучшей читаемости коды и снижения риска нежелательных видов, оборонительный подход рекомендуются, где переменные явно приводятся к нужному типу.


Смотрите также:

переупорядочить ND-массив может улучшить общую производительность

В некоторых случаях нам нужно применять функции к набору ND-массивов. Давайте посмотрим на этот простой пример.

A(:,:,1) = [1 2; 4 5];
A(:,:,2) = [11 22; 44 55];
B(:,:,1) = [7 8; 1 2];
B(:,:,2) = [77 88; 11 22];

A =

ans(:,:,1) =

   1   2 
   4   5 

ans(:,:,2) =

   11   22
   44   55

>> B
B =

ans(:,:,1) =

   7   8
   1   2

ans(:,:,2) =

   77   88
   11   22

Обе матрицы 3D, скажем, мы должны вычислить следующее:

result= zeros(2,2);
...
for k = 1:2 
   result(i,j) = result(i,j) + abs( A(i,j,k) - B(i,j,k) );
...

if k is very large, this for-loop can be a bottleneck since MATLAB order the data in a column major fashion. So a better way to compute "result" could be:

% trying to exploit the column major ordering
Aprime = reshape(permute(A,[3,1,2]), [2,4]);
Bprime = reshape(permute(B,[3,1,2]), [2,4]);


>> Aprime
Aprime =

    1    4    2    5
   11   44   22   55

>> Bprime
Bprime =

    7    1    8    2
   77   11   88   22

Теперь мы заменим вышеприведенный цикл следующим образом:

result= zeros(2,2);
....
temp = abs(Aprime - Bprime);
for k = 1:2
    result(i,j) = result(i,j) + temp(k, i+2*(j-1));
...

Мы перегруппировали данные, чтобы мы могли использовать кэш-память. Пермутация и изменение могут быть дорогостоящими, но при работе с большими ND-массивами вычислительная стоимость, связанная с этими операциями, намного ниже, чем работа с не организованными массивами.

Важность предварительного распределения

Массивы в MATLAB хранятся в виде непрерывных блоков в памяти, которые автоматически выделяются и выпускаются MATLAB. MATLAB скрывает операции управления памятью, такие как изменение размера массива за простым в использовании синтаксисом:

a = 1:4

a =

     1     2     3     4

a(5) = 10  % or alternatively a = [a, 10]

a =

     1     2     3     4    10

Важно понимать, что вышеизложенное не является тривиальной операцией, a(5) = 10 заставит MATLAB выделить новый блок памяти размером 5, скопировать первые 4 числа и установить 5'-10. Это операция O(numel(a)) , а не O(1) .

Рассмотрим следующее:

clear all
n=12345678;
a=0;
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 3.004213 seconds.

a перераспределяется n раз в этом цикле (исключая некоторые оптимизации, предпринятые MATLAB)! Обратите внимание, что MATLAB дает нам предупреждение:

«Переменная« a », по-видимому, меняет размер на каждой итерации цикла. Подумайте о предварительном распределении для скорости».

Что происходит, когда мы предварительно распределяем?

a=zeros(1,n);
tic
for i = 2:n
    a(i) = sqrt(a(i-1)) + i;
end
toc

Elapsed time is 0.410531 seconds.

Мы видим, что время выполнения сокращается на порядок.

Методы предварительного распределения:

MATLAB предоставляет различные функции для размещения векторов и матриц в зависимости от конкретных требований пользователя. К ним относятся: zeros , ones , nan , eye , true и т. Д.

a = zeros(3)       % Allocates a 3-by-3 matrix initialized to 0
a =

     0     0     0
     0     0     0
     0     0     0

a = zeros(3, 2)     % Allocates a 3-by-2 matrix initialized to 0
a =

     0     0
     0     0
     0     0

a = ones(2, 3, 2)      % Allocates a 3 dimensional array (2-by-3-by-2) initialized to 1
a(:,:,1) =

     1     1     1
     1     1     1


a(:,:,2) =

     1     1     1
     1     1     1

a = ones(1, 3) * 7  % Allocates a row vector of length 3 initialized to 7
a =

     7     7     7

Также может быть указан тип данных:

a = zeros(2, 1, 'uint8');  % allocates an array of type uint8

Также легко клонировать размер существующего массива:

a = ones(3, 4);       % a is a 3-by-4 matrix of 1's
b = zeros(size(a));  % b is a 3-by-4 matrix of 0's

И клонируйте тип:

a = ones(3, 4, 'single');       % a is a 3-by-4 matrix of type single
b = zeros(2, 'like', a);        % b is a 2-by-2 matrix of type single

обратите внимание, что «как» также клонирует сложность и разреженность .

Предварительное выделение неявно достигаются с помощью какой - либо функции , которая возвращает массив конечного требуемого размера, например, rand , gallery , kron , bsxfun , colon и многие другие. Например, общий способ выделения векторов с линейно изменяющимися элементами - это использование оператора двоеточия (с вариантом 2 или 3-операнда 1 ):

a = 1:3 
a =

     1     2     3

a = 2:-3:-4
a =

     2    -1    -4

Ячейные массивы могут быть выделены с помощью функции cell() почти так же, как и zeros() .

a = cell(2,3)
a = 

    []    []    []
    []    []    []

Обратите внимание, что массивы ячеек работают, удерживая указатели в ячейках памяти в содержимом ячейки. Таким образом, все подсказки preallocation применяются также к отдельным элементам массива ячеек.


Дальнейшее чтение:



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow