Buscar..


Observaciones

  • El código de perfil es una forma de evitar la temida práctica de la " optimización prematura ", al enfocar al desarrollador en aquellas partes del código que realmente justifican los esfuerzos de optimización.
  • Artículo de documentación de MATLAB titulado " Medir el rendimiento de su programa ".

Identificar cuellos de botella de rendimiento utilizando el Perfilador

El perfilador de MATLAB es una herramienta para la creación de perfiles de software de código MATLAB. Usando el Perfilador, es posible obtener una representación visual del tiempo de ejecución y del consumo de memoria.

Ejecutar el Perfilador se puede hacer de dos maneras:

  • Al hacer clic en el botón "Ejecutar y tiempo" en la GUI de MATLAB mientras se abre algún archivo .m en el editor (agregado en R2012b ).

    Botón en la barra de herramientas

  • Programáticamente, utilizando:

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

A continuación se muestra un código de ejemplo y el resultado de su perfil:

function docTest

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

spy

Salida de perfil

De lo anterior, aprendemos que la función de spy ocupa aproximadamente el 25% del tiempo total de ejecución. En el caso de "código real", una función que tome un porcentaje tan grande de tiempo de ejecución sería un buen candidato para la optimización, en lugar de funciones análogas a var y cla cuya optimización debería evitarse.

Además, es posible hacer clic en las entradas en la columna Nombre de la función para ver un desglose detallado del tiempo de ejecución de esa entrada. Aquí está el ejemplo de hacer clic spy :

Tiempo interno de "espía"


También es posible perfilar el consumo de memoria ejecutando el profile('-memory') antes de ejecutar el Perfilador.

introduzca la descripción de la imagen aquí

Comparando el tiempo de ejecución de múltiples funciones

La combinación ampliamente utilizada de tic y toc puede proporcionar una idea aproximada del tiempo de ejecución de una función o fragmentos de código.

Para comparar varias funciones no debe utilizarse. ¿Por qué? Es casi imposible proporcionar las mismas condiciones para que todos los fragmentos de código se comparen dentro de un script utilizando la solución anterior. Tal vez las funciones comparten el mismo espacio de funciones y variables comunes, por lo que más adelante las funciones denominadas fragmentos de código ya aprovechan las variables y funciones previamente iniciadas. Además, no hay ninguna idea de si el compilador JIT manejaría estos fragmentos posteriormente llamados igualmente.


La función dedicada para los puntos de referencia es el timeit . El siguiente ejemplo ilustra su uso.

Están la matriz A y la matriz B Se debe determinar qué fila de B es la más similar a A contando el número de elementos diferentes.

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

Esta forma de referencia fue vista por primera vez en esta respuesta .

¡Está bien ser 'soltero'!

Visión general:

El tipo de datos predeterminado para matrices numéricas en MATLAB es double . double es una representación de números en coma flotante , y este formato toma 8 bytes (o 64 bits) por valor. En algunos casos, donde, por ejemplo, tratar solo con números enteros o cuando la inestabilidad numérica no es un problema inminente, tal profundidad de bit alta puede no ser necesaria. Por este motivo, se recomienda considerar los beneficios de la precisión single (u otros tipos apropiados):

  • Tiempo de ejecución más rápido (especialmente notable en las GPU).
  • La mitad del consumo de memoria: puede tener éxito cuando el double falla debido a un error de falta de memoria; Más compacto cuando se almacena como archivos.

La conversión de una variable de cualquier tipo de datos compatible a uno single se realiza mediante:

sing_var = single(var);

Algunas funciones de uso común (como zeros , eye , ones , etc. ) que emiten valores double de forma predeterminada, permiten especificar el tipo / clase de la salida.

Conversión de variables en una secuencia de comandos a una precisión / tipo / clase no predeterminada:

A partir de julio de 2016, no existe una forma documentada de cambiar el tipo de datos MATLAB predeterminado de double .

En MATLAB, las nuevas variables generalmente imitan los tipos de datos de las variables utilizadas al crearlas. Para ilustrar esto, considere el siguiente ejemplo:

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  

Por lo tanto, puede parecer suficiente convertir / convertir varias variables iniciales para que el cambio esté permeado en todo el código, sin embargo, esto no es recomendable (consulte Advertencias y errores a continuación).

Advertencias y escollos:

  1. Las conversiones repetidas se desaconsejan debido a la introducción de ruido numérico (cuando se convierte de single a double ) o la pérdida de información (cuando se convierte de double a single , o entre ciertos tipos de enteros ), por ejemplo:

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

    Esto puede ser mitigado un poco usando typecast . Consulte también Tenga en cuenta la inexactitud del punto flotante .

  2. Se desaconseja confiar únicamente en la tipificación de datos implícita (es decir, lo que MATLAB adivina sobre el tipo de salida de un cálculo) se desaconseja debido a varios efectos no deseados que pueden surgir:

    • Pérdida de información : cuando se espera un resultado double , pero una combinación descuidada de operandos single y double produce single precisión single .

    • Consumo de memoria inesperadamente alto : cuando se espera un single resultado pero un cálculo descuidado da como resultado una salida double .

    • Gastos generales innecesarios cuando se trabaja con GPU : cuando se mezclan los tipos gpuArray (es decir, las variables almacenadas en VRAM) con variables que no son gpuArray (es decir, las que normalmente se almacenan en la RAM), los datos deberán transferirse de una forma u otra antes de poder realizar el cálculo. Esta operación lleva tiempo y puede ser muy notable en los cálculos repetitivos.

    • Errores al mezclar tipos de punto flotante con tipos enteros : las funciones como mtimes ( * ) no están definidas para entradas mixtas de tipos enteros y de punto flotante, y aparecerán errores. Las funciones como times ( .* ) No están definidas en absoluto para entradas de tipo entero, y volverán a generar un error.

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

    Para una mejor legibilidad del código y un menor riesgo de tipos no deseados, se recomienda un enfoque defensivo, donde las variables se convierten explícitamente al tipo deseado.


Ver también:

reorganizar una matriz ND puede mejorar el rendimiento general

En algunos casos, debemos aplicar funciones a un conjunto de matrices ND. Veamos este sencillo ejemplo.

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

Ambas matrices son 3D, digamos que tenemos que calcular lo siguiente:

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

Ahora reemplazamos el bucle anterior por lo siguiente:

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

Reorganizamos los datos para poder explotar la memoria caché. La permutación y la reforma pueden ser costosas, pero cuando se trabaja con grandes matrices ND, el costo computacional relacionado con estas operaciones es mucho más bajo que trabajar con matrices no organizadas.

La importancia de la preasignación.

Las matrices en MATLAB se mantienen como bloques continuos en memoria, asignados y liberados automáticamente por MATLAB. MATLAB oculta las operaciones de administración de memoria, como el cambio de tamaño de una matriz con una sintaxis fácil de usar:

a = 1:4

a =

     1     2     3     4

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

a =

     1     2     3     4    10

Es importante entender que lo anterior no es una operación trivial, a(5) = 10 hará que MATLAB asigne un nuevo bloque de memoria de tamaño 5, copie los primeros 4 números y establezca el 5'th a 10. Es una O(numel(a)) , y no O(1) .

Considera lo siguiente:

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 se reasigna n veces en este bucle (excluyendo algunas optimizaciones realizadas por MATLAB)! Tenga en cuenta que MATLAB nos da una advertencia:

"La variable 'a' parece cambiar de tamaño en cada iteración de bucle. Considere la posibilidad de preasignar para la velocidad".

¿Qué pasa cuando nos preasignamos?

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

Elapsed time is 0.410531 seconds.

Podemos ver que el tiempo de ejecución se reduce en un orden de magnitud.

Métodos de preasignación:

MATLAB proporciona varias funciones para la asignación de vectores y matrices, dependiendo de los requisitos específicos del usuario. Estos incluyen: zeros , ones , nan , eye , true , etc.

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

También se puede especificar un tipo de datos:

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

También es fácil clonar el tamaño de una matriz existente:

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

Y clonar el tipo:

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

tenga en cuenta que 'me gusta' también clona la complejidad y la escasez .

La preasignación se logra implícitamente mediante cualquier función que devuelva una matriz del tamaño final requerido, como rand , gallery , kron , bsxfun , colon y muchos otros. Por ejemplo, una forma común de asignar vectores con elementos que varían linealmente es mediante el uso del operador de dos puntos (con la variante 1 o 2 del operando 3 ):

a = 1:3 
a =

     1     2     3

a = 2:-3:-4
a =

     2    -1    -4

Los arreglos de celdas pueden asignarse usando la función cell() de la misma manera que los zeros() .

a = cell(2,3)
a = 

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

Tenga en cuenta que las matrices de celdas funcionan al mantener los punteros en las ubicaciones en la memoria del contenido de la celda. Por lo tanto, todas las sugerencias de preasignación se aplican también a los elementos de la matriz de celdas individuales.


Otras lecturas:



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow