Eu estou experimentando com MATLAB OOP , como um começo eu imitava o meu C ++ 's aulas Logger e eu estou colocando todas as minhas funções auxiliares de corda em uma classe String, pensando que seria ótimo ser capaz de fazer coisas como a + b
, a == b
, a.find( b )
em vez de strcat( a b )
, strcmp( a, b )
, recuperar o primeiro elemento de strfind( a, b )
etc.
O problema: desaceleração
Coloquei as coisas acima em uso e imediatamente notei uma desaceleração drástica . Estou fazendo errado (o que é certamente possível, pois tenho uma experiência bastante limitada com o MATLAB) ou o POO do MATLAB apenas introduz muita sobrecarga?
Meu caso de teste
Aqui está o teste simples que fiz para a string, basicamente apenas acrescentando uma string e removendo a parte anexada novamente:
Nota: Na verdade, não escreva uma classe String como essa em código real! O Matlab agora tem um
string
tipo de matriz nativa , e você deve usá-lo.
classdef String < handle
....
properties
stringobj = '';
end
function o = plus( o, b )
o.stringobj = [ o.stringobj b ];
end
function n = Length( o )
n = length( o.stringobj );
end
function o = SetLength( o, n )
o.stringobj = o.stringobj( 1 : n );
end
end
function atest( a, b ) %plain functions
n = length( a );
a = [ a b ];
a = a( 1 : n );
function btest( a, b ) %OOP
n = a.Length();
a = a + b;
a.SetLength( n );
function RunProfilerLoop( nLoop, fun, varargin )
profile on;
for i = 1 : nLoop
fun( varargin{ : } );
end
profile off;
profile report;
a = 'test';
aString = String( 'test' );
RunProfilerLoop( 1000, @(x,y)atest(x,y), a, 'appendme' );
RunProfilerLoop( 1000, @(x,y)btest(x,y), aString, 'appendme' );
Os resultados
Tempo total em segundos, para 1000 iterações:
btest 0,550 (com String.SetLength 0,138, String.plus 0,065, String.Length 0,057)
atest 0,015
Os resultados para o sistema de logger também são: 0,1 segundos para 1000 chamadas para frpintf( 1, 'test\n' )
, 7 (!) Segundos para 1000 chamadas para o meu sistema ao usar a classe String internamente (OK, ele possui muito mais lógica, mas para comparar com C ++: a sobrecarga do meu sistema que usa std::string( "blah" )
e std::cout
no lado da saída vs simples std::cout << "blah"
é da ordem de 1 milissegundo.)
É uma sobrecarga ao procurar funções de classe / pacote?
Como o MATLAB é interpretado, ele deve procurar a definição de uma função / objeto em tempo de execução. Então, eu queria saber que talvez haja muito mais sobrecarga na procura de funções de classe ou pacote vs funções que estão no caminho. Eu tentei testar isso, e isso fica mais estranho. Para descartar a influência de classes / objetos, comparei a chamada de uma função no caminho versus uma função em um pacote:
function n = atest( x, y )
n = ctest( x, y ); % ctest is in matlab path
function n = btest( x, y )
n = util.ctest( x, y ); % ctest is in +util directory, parent directory is in path
Resultados, reunidos da mesma forma que acima:
atest 0,004 s, 0,001 s no ctest
btest 0,060 s, 0,014 s em util.ctest
Então, toda essa sobrecarga é proveniente do MATLAB gastando tempo procurando definições para sua implementação OOP, enquanto essa sobrecarga não existe para funções diretamente no caminho?
for i = 1:this.get_n_quantities() if(strcmp(id,this.get_quantity_rlz(i).get_id())) ix = i; end end
leva 2.2 segundos, enquantonq = this.get_n_quantities(); a = this.get_quantity_realizations(); for i = 1:nq c = a{i}; if(strcmp(id,c.get_id())) ix = i; end end
toma 0.01, duas ordens de magRespostas:
Trabalho com o OO MATLAB há um tempo e acabei analisando problemas de desempenho semelhantes.
A resposta curta é: sim, o POO do MATLAB é meio lento. Existe uma sobrecarga substancial de chamadas de método, mais alta que as linguagens OO convencionais, e não há muito o que fazer sobre isso. Parte do motivo pode ser que o MATLAB idiomático use código "vetorizado" para reduzir o número de chamadas de método, e a sobrecarga por chamada não é uma alta prioridade.
Avaliei o desempenho escrevendo funções "nop" do-nothing como os vários tipos de funções e métodos. Aqui estão alguns resultados típicos.
Resultados semelhantes no R2008a a R2009b. Este é no Windows XP x64 executando o MATLAB de 32 bits.
O "Java nop ()" é um método Java do-nothing chamado de dentro de um loop de código M e inclui a sobrecarga de despacho MATLAB para Java a cada chamada. "Java nop () de Java" é a mesma coisa chamada em um loop Java for () e não incorre nessa penalidade de limite. Faça os intervalos de Java e C com um grão de sal; um compilador inteligente pode otimizar completamente as chamadas.
O mecanismo de escopo do pacote é novo, introduzido aproximadamente ao mesmo tempo que as classes classdef. Seu comportamento pode estar relacionado.
Algumas conclusões provisórias:
obj.nop()
sintaxe é mais lenta que anop(obj)
sintaxe, mesmo para o mesmo método em um objeto classdef. O mesmo para objetos Java (não mostrado). Se você quiser ir mais rápido, liguenop(obj)
.Dizer por que isso é assim seria apenas especulação da minha parte. Os internos de OO do mecanismo MATLAB não são públicos. Não é um problema interpretado versus compilado em si - o MATLAB possui um JIT -, mas a tipagem e sintaxe mais frouxas do MATLAB podem significar mais trabalho em tempo de execução. (Por exemplo, você não pode distinguir apenas da sintaxe "f (x)" é uma chamada de função ou um índice em uma matriz; depende do estado da área de trabalho no tempo de execução.) Pode ser porque as definições de classe do MATLAB estão vinculadas ao estado do sistema de arquivos de uma maneira que muitas outras línguas não o são.
Então o que fazer?
Uma abordagem idiomática do MATLAB para isso é "vetorizar" seu código estruturando suas definições de classe de modo que uma instância de objeto envolva uma matriz; isto é, cada um de seus campos contém matrizes paralelas (denominadas organização "planar" na documentação do MATLAB). Em vez de ter uma matriz de objetos, cada um com campos contendo valores escalares, define objetos que são matrizes e os métodos tomam matrizes como entradas e fazem chamadas vetorizadas nos campos e entradas. Isso reduz o número de chamadas de método feitas, espero que o gasto adicional da expedição não seja um gargalo.
Imitar uma classe C ++ ou Java no MATLAB provavelmente não será o ideal. As classes Java / C ++ são tipicamente construídas de modo que os objetos sejam os menores blocos de construção, o mais específico possível (ou seja, muitas classes diferentes), e você os compõe em matrizes, objetos de coleção etc. e itera sobre eles com loops. Para fazer aulas rápidas do MATLAB, vire essa abordagem do avesso. Tenha classes maiores cujos campos são matrizes e chame métodos vetorizados nessas matrizes.
O objetivo é organizar seu código para que ele atenda aos pontos fortes da linguagem - manipulação de array, matemática vetorizada - e evite os pontos fracos.
EDIT: Desde o post original, R2010b e R2011a foram lançados. A imagem geral é a mesma, com as chamadas do MCOS ficando um pouco mais rápidas e as chamadas do Java e do método antigo ficando mais lentas .
EDIT: Eu costumava fazer algumas anotações aqui sobre "sensibilidade do caminho" com uma tabela adicional de tempos de chamada de função, onde os tempos da função eram afetados pela forma como o caminho do Matlab foi configurado, mas isso parece ter sido uma aberração da minha configuração de rede específica em A Hora. O gráfico acima reflete os horários típicos da preponderância dos meus testes ao longo do tempo.
Atualização: R2011b
EDIT (13/2/2012): O R2011b foi lançado e a imagem de desempenho mudou o suficiente para atualizar isso.
Eu acho que o resultado disso é o seguinte:
foo(obj)
sintaxe. Portanto, a velocidade do método não é mais um motivo para seguir as classes de estilo antigo na maioria dos casos. (Parabéns, MathWorks!)Atualização: R2014a
Eu reconstruí o código de benchmarking e o executei no R2014a.
Atualização: R2015b: os objetos ficaram mais rápidos!
Aqui estão os resultados do R2015b, gentilmente fornecidos por @Shaked. Essa é uma grande mudança: OOP é significativamente mais rápido, e agora a
obj.method()
sintaxe é tão rápida quantomethod(obj)
e muito mais rápida que os objetos OOP herdados.Atualização: R2018a
Aqui estão os resultados do R2018a. Não é o grande salto que vimos quando o novo mecanismo de execução foi introduzido no R2015b, mas ainda é uma melhoria apreciável ano após ano. Notavelmente, identificadores de funções anônimas ficaram muito mais rápidos.
Atualização: R2018b e R2019a: nenhuma alteração
Sem alterações significativas. Não estou me incomodando em incluir os resultados do teste.
Código Fonte para Benchmarks
Coloquei o código-fonte desses benchmarks no GitHub, lançado sob a licença MIT. https://github.com/apjanke/matlab-bench
fonte
A classe handle tem uma sobrecarga adicional ao rastrear todas as referências a si mesma para fins de limpeza.
Experimente o mesmo experimento sem usar a classe handle e veja quais são seus resultados.
fonte
O desempenho do OO depende significativamente da versão do MATLAB usada. Não posso comentar em todas as versões, mas sei por experiência que 2012a foi muito aprimorado em relação às versões de 2010. Sem referências e, portanto, sem números para apresentar. Meu código, escrito exclusivamente usando classes de identificador e escrito em 2012a, não será executado nas versões anteriores.
fonte
Na verdade, não há problema com o seu código, mas é um problema com o Matlab. Eu acho que é uma espécie de brincadeira para parecer. Não é nada além de sobrecarga para compilar o código da classe. Eu fiz o teste com ponto de classe simples (uma vez como identificador) e o outro (uma vez como classe de valor)
aqui está o teste
Os resultados t1 =
12.0212% Alça
t2 =
12.0042% valor
t3 =
t4 =
Portanto, para obter um desempenho eficiente, evite usar OOP. A estrutura é uma boa opção para agrupar variáveis
fonte