Профилируйте свой код

Блокноты MuPAD® будут демонтированы в будущем релизе. Используйте live скрипты MATLAB® вместо этого.

Live скрипты MATLAB поддерживают большую часть функциональности MuPAD, хотя существуют некоторые различия. Для получения дополнительной информации смотрите, Преобразовывают Notebook MuPAD в Live скрипты MATLAB.

Профилирование является способом измериться, где программа проводит время. MuPAD® обеспечивает функцию prog::profile, чтобы помочь вам идентифицировать узкие места производительности в своем коде. Используйте эту функцию, чтобы анализировать производительность сложных вложенных вызовов процедуры. Для более простых фрагментов кода измерение времен выполнения более удобно и может дать вам достаточно информации, чтобы идентифицировать узкие места производительности.

Функция prog::profile оценивает код, который вы хотите профилировать, и возвращает профильный отчет. Отчет содержит:

  • Таблица, показывающая время ваш код, потраченный на каждую функцию (общее количество и усредненный на один вызов), количество вызовов этой функции, дочерних элементов этой функции, и так далее. Информация для наиболее в большой степени используемых функций появляется на верхних строках таблицы. Первая строка в этой таблице представляет общее время выполнения.

  • График зависимости, показывающий саму функцию, функции, которые это вызывает (дочерние элементы) и функции, которые вызывают эту функцию. График также показывает синхронизацию и количество призывов к каждой функции.

Примечание

По умолчанию prog::profile не измеряет уровень одного вызовов функций ядра.

Однако, когда prog::profile измеряет уровень библиотечных функций, это также распечатывает накопленное время, которое система проводит в функциях ядра. Чтобы измерить уровень одного вызова функции ядра, используйте prog::trace, чтобы проследить ту функцию ядра.

Предположим, что вы хотите записать процедуру, которая проверяет, появляется ли каждое целое число от 1 до 1 000 в 1000×1000 матрица случайных целых чисел. Прямой подход должен записать процедуру, которая проверяет каждый элемент матрицы, продолжающей строка строкой и столбцом столбцом:

f := proc(M, n, x)
begin
  for j from 1 to n do
    for k from 1 to n do
      if M[j, k] = x then
        return(TRUE)
      end_if
    end_for
  end_for;
  return(FALSE)
end_proc:

Используйте linalg::randomMatrix, чтобы создать 1000×1000 матрица случайных целых чисел:

matrixSize := 1000:
M := linalg::randomMatrix(matrixSize, matrixSize, Dom::Integer):

Затем вызовите процедуру f 1000 раз, чтобы проверять, появляется ли каждый номер от 1 до 1 000 в той матрице:

g := proc()
begin
  f(M, matrixSize, i) $ i = 1..1000
end_proc:

Измерение времени должно было запустить процедуру, g показывает, что процедура требует оптимизации. Несмотря на то, что узкое место производительности в этой процедуре очевидно, не всегда легко идентифицировать узкие места производительности в более сложных процедурах. Функция time не указывает, где процедура проводит большую часть своего времени выполнения:

time(g())
 62023.876 

Чтобы получить полный профильный отчет, который показывает синхронизации для всех внутренних вызовов функции, используйте prog::profile:

prog::profile(g()):
  percent usage of all
    |      time self per single call
    |          |      time self
    |          |          |      time children per single
call
    |          |          |
         |      time children
  
 |          |          |          |          |     calls/normal exit
    |          |          |          |    
     |        |   calls/remember exit
 
  |          |          |          |          |        |   |  calls/errors
    |          |          |          |    
     |        |   |  |  [index] function name
--------------------------------------------------------------------------------------------------------------------
 100.0   109982.9   109982.9         .    
     .         1  .  .  [0]  procedure entry point               
       
--------------------------------------------------------------------------------------------------------------------
  45.9         .     50467.2         .    
27149.6  3019825  .  .  [1]  (Dom::Matrix(Dom::Integer))::_index_intern
 
  19.7         .     21689.3  
      .      5460.3  3019825  .  .  [2]  Dom::Integer::coerce    
                   
  16.7      
  .     18373.1         .     77616.9  3019825  .  .  [3]  (Dom::Matrix(Dom::Integer))::_index
        
  12.7       14.0    13984.8
      96.0    95990.0     1000  .  .  [4]  f                     
                     
   5.0    
    .      5460.3         .          .   3019825  .  .  [5]  Dom::Integer::convert
                      
    .    
    8.0        8.0   109974.9   109974.9        1  .  .  [6]  g  
                                        
--------------------------------------------------------------------------------------------------------------------
 
index  
  %time      self  children     called  [index] name
---------------------------------------------------------------------------------------------
  [0]     100.0  109982.8         0       
  1  procedure entry point
     
                8.0  109974.8          1      [6]  g
---------------------------------------------------------------------------------------------
                 50467.24  27149.65    3019825
     [3]  (Dom::Matrix(Dom::Integer))::_index
  [1]      45.9  50467.24  27149.65    3019825  (Dom::Matrix(Dom::Integer))::_index_intern
                 21689.30  5460.346    3019825
     [2]  Dom::Integer::coerce
---------------------------------------------------------------------------------------------
                 21689.30  5460.346    3019825
     [1]  (Dom::Matrix(Dom::Integer))::_index_intern
  [2]      19.7  21689.30  5460.346    3019825
 Dom::Integer::coerce
          
      5460.346         0    3019825      [5]  Dom::Integer::convert
---------------------------------------------------------------------------------------------
                 18373.13  77616.89    3019825
     [4]  f
  [3]      16.7  18373.13
 77616.89    3019825  (Dom::Matrix(Dom::Integer))::_index
                 50467.24  27149.65    3019825
     [1]  (Dom::Matrix(Dom::Integer))::_index_intern
---------------------------------------------------------------------------------------------
                 13984.84  95990.02       1000
     [6]  g
  [4]      12.7  13984.84
 95990.02       1000  f
        
        18373.13  77616.89    3019825      [3]  (Dom::Matrix(Dom::Integer))::_index
---------------------------------------------------------------------------------------------
                 5460.346         0    3019825
     [2]  Dom::Integer::coerce
 
[5]       5.0  5460.346         0    3019825  Dom::Integer::convert
---------------------------------------------------------------------------------------------
  [6]       0.0       8.0  109974.8       
  1  g
                 13984.84
 95990.02       1000      [4]  f
---------------------------------------------------------------------------------------------
 Time sum: 109982.873 ms

Верхние строки профильного отчета указывают, что процедура проводит большую часть своего времени, получая доступ к каждому элементу матрицы. Чтобы улучшать производительность, перепишите процедуру так, чтобы это могло получить доступ к меньшему количеству элементов матрицы в каждом вызове процедуры f. Например, используйте алгоритм на основе метода деления пополам:

f := proc(M, n, x)
begin
  if (M[1] - x)*(M[n] - x) > 0 then
    return(FALSE)
  elif (M[1] - x)*(M[n] - x) = 0 then
    return(TRUE);
  else
    a := 1: b := n:
    while (b - a > 1) do
      if is(b - a, Type::Odd) then
        c := a + (b - a + 1)/2
      else
        c := a + (b - a)/2
      end_if;
      if M[c] - x = 0 then
        return(TRUE)
      elif (M[a] - x)*(M[c] - x) < 0 then
        b := c:  
      else
        a := c:
      end_if;
    end_while;
  end_if;
  return(FALSE)
end_proc:

Прежде, чем вызвать процедуру f, необходимо преобразовать матричный M в список и вид тот список. Сортировка списка, который содержит 104 записи, является дорогой операцией. В зависимости от количества вызовов процедуры f эта операция может потенциально устранить увеличение производительности, которую вы получаете путем улучшения процедуры сам f:

g := proc()
local M1;
begin
  M1 := sort([op(M)]):
  f(M1, matrixSize^2, i) $ i = 1..1000
end_proc:

Для них конкретный матричный размер и количество вызовов f, реализовывая bisectional алгоритм все еще эффективны несмотря на время, требуемое отсортировать список:

time(g())
 3840.24 

Профильный отчет показывает, что процедура тратит наиболее часто выполнение вызовы функции g и op. Это вызвано тем, что реализация алгоритма двоичного поиска добавила новые дорогие операции в g (преобразование матрицы к списку и затем сортировке списка). Профильный отчет, сгенерированный для вызова процедуры g(), очень длинен. Этот пример показывает только верхнюю часть отчета:

prog::profile(g()):
  percent usage of all
    |     time self per single call
    |        |     time self
    |        |        |     time children per single
call
    |        |        |    
   |     time children
    |    
   |        |        |        |    calls/normal exit
    |        |        |        |        | 
   |    calls/remember exit
    |
       |        |        |        |     |     |   calls/errors
    |        |        |        |        | 
   |     |   |  [index] function name
-------------------------------------------------------------------------------------------------
 100.0   3884.2   3884.2       .        . 
    1     .  .  [0]  procedure entry point            
-------------------------------------------------------------------------------------------------
  56.1   2180.1   2180.1   1704.1   1704.1
    1     .  .  [1]  g                                
  33.0   1280.1   1280.1    188.0    188.0
    1     .  .  [2]  (Dom::Matrix(Dom::Integer))::op  
   6.1      0.2    236.0       .        . 
 1000     .  .  [3]  f                                
   3.2      0.1    124.0       .        . 
 1000     .  .  [4]  `p -> [coeff(p, All)][2..-1]`    
   1.5      0.1     60.0       .        . 
 1000     .  .  [5]  `l -> l.[Rzero $ r - nops(l)]`   
   0.1      2.0      4.0       .        . 
    2     .  .  [6]  Dom::Integer::hasProp            
    .        .        .        .        . 
    2     .  .  [7]  DomainConstructor::hasProp       
    .        .        .        .        . 
    .  9981  .  [8]  is                               
-------------------------------------------------------------------------------------------------
 

Рекомендуемый подход для того, чтобы улучшать производительность вашего кода должен использовать функции MuPAD, если это возможно. Например, MuPAD обеспечивает функцию has для проверки, содержит ли один объект MuPAD другой объект MuPAD. Перепишите свой код с помощью функции has и комбинируя процедуры f и g:

g := proc()
local M1;
begin
  M1 := {op(M)}:
  has(M1, i) $ i = 1..1000
end_proc:

Эта процедура также преобразовывает матричный M в набор. Преобразование матрицы к набору может уменьшать число элементов. (MuPAD удаляет дублирующиеся элементы набора.)

Время выполнения для вызова процедуры g() является самым коротким среди этих трех реализаций:

time(g())
 1520.095 

Профильный отчет показывает, что процедура проводит большую часть своего времени выполнения, получая доступ 1000×1000 матрица случайных целых чисел и преобразовывая его в набор. Этот пример показывает только верхнюю часть профильного отчета:

prog::profile(g()):
  percent usage of all
    |     time self per single call
    |        |     time self
    |        |        |     time children per single
call
    |        |        |    
   |     time children
    |    
   |        |        |        |    calls/normal exit
    |        |        |        |        | 
   |   calls/remember exit
    |
       |        |        |        |     |   |  calls/errors
    |        |        |        |        | 
   |   |  |  [index] function name
----------------------------------------------------------------------------------------------
 100.0   1556.1   1556.1       .        . 
    1  .  .  [0]  procedure entry point            
----------------------------------------------------------------------------------------------
  78.9   1228.1   1228.1    188.0    188.0
    1  .  .  [1]  (Dom::Matrix(Dom::Integer))::op  
   9.0    140.0    140.0   1416.1   1416.1     1  . 
.  [2]  g                                
   8.2      0.1    128.0       .        .   1000  . 
.  [3]  `p -> [coeff(p, All)][2..-1]`    
   3.9      0.1     60.0       .        .   1000  . 
.  [4]  `l -> l.[Rzero $ r - nops(l)]`   
    .        .        .        .        .      2  . 
.  [5]  Dom::Integer::hasProp            
    .        .        .        .        .      2  . 
.  [6]  DomainConstructor::hasProp       
----------------------------------------------------------------------------------------------
 
Для просмотра документации необходимо авторизоваться на сайте