Discussão do artigo "Gerenciamento Avançado de Memória e Técnicas de Otimização em MQL5"

 

Novo artigo Gerenciamento Avançado de Memória e Técnicas de Otimização em MQL5 foi publicado:

Descubra técnicas práticas para otimizar o uso de memória em sistemas de negociação MQL5. Aprenda a construir Expert Advisors e indicadores eficientes, estáveis e com alto desempenho. Exploraremos como a memória realmente funciona no MQL5, as armadilhas comuns que desaceleram seus sistemas ou causam falhas e, mais importante ainda, como corrigi-las.

O MQL5 é inegavelmente poderoso, mas com esse poder vem responsabilidade, especialmente quando se trata de memória. Muitos desenvolvedores focam exclusivamente na lógica da estratégia, nos pontos de entrada e no gerenciamento de risco, enquanto o tratamento da memória silenciosamente se transforma em uma bomba-relógio em segundo plano. À medida que seu código cresce, processando mais símbolos, frequências mais altas e conjuntos de dados mais pesados, ignorar a memória pode levar a gargalos de desempenho, instabilidade e oportunidades perdidas.

Neste artigo, vamos analisar o que acontece por trás dos bastidores. Exploraremos como a memória realmente funciona no MQL5, as armadilhas comuns que desaceleram seus sistemas ou causam falhas e, mais importante ainda, como corrigi-las. Você aprenderá técnicas práticas de otimização que tornam seus programas de negociação mais rápidos, mais leves e mais confiáveis.

Veja onde o uso eficiente de memória realmente faz diferença:

  • Negociação de Alta Frequência: Cada milissegundo é uma vantagem potencial ou uma perda potencial.

  • Análise Multitemporal: Combinando gráficos? Espere que a pressão sobre a memória se multiplique.

  • Lógica Complexa de Indicadores: Matemática avançada e grandes conjuntos de dados podem fazer tudo parar se não forem gerenciados adequadamente.

  • Backtests com Grandes Históricos: Sem uma otimização inteligente, os backtests podem parecer tão lentos quanto assistir tinta secar.

Se você está pronto para levar desempenho a sério, vamos mergulhar no assunto e tornar seus sistemas MQL5 tão eficientes quanto inteligentes.



Autor: Sahil Bagdi

 

O artigo parece bastante discutível (apenas alguns pontos).

Qual é a classe que você mencionou aqui?

// Variável de membro da classe - created once
double prices[];

void OnTick()
{
   // Reutilizar a matriz existente
   for(int i = 0; i < 1000; i++)
   {
      prices[i] = iClose(_Symbol, PERIOD_M1, i);
   }
   
   // Processar os dados...
}

Pela presença do manipulador OnTick e pela forma como a matriz é acessada, fica implícito que você adicionou a matriz de preços ao escopo global, o que é uma má ideia (devido à poluição do namespace, caso a matriz seja necessária apenas no escopo do manipulador). Provavelmente seria mais apropriado manter o código inicial do mesmo exemplo, mas tornar a matriz estática; dessa forma, todos veriam claramente a diferença:

// Abordagem eficiente (aloca a matriz uma única vez, podendo ajustar seu tamanho se necessário)
void OnTick()
{
   // Isso NÃO create (nor allocate) array on every tick
   static double prices[];
   ArrayResize(prices, 1000);
   
   // Preencher a matriz com os dados de preços
   for(int i = 0; i < 1000; i++)
   {
      prices[i] = iClose(_Symbol, PERIOD_M1, i);
   }
   
   // Processar os dados...
}

Além disso, se você substituir Array of Structures (AoS) por Structure of Arrays (SoA) para OHLCV — o acesso aos preços da mesma barra precisa de mais referências (alternando entre matrizes em vez de incrementar o deslocamento dentro de uma única estrutura) e torna o processo mais lento, mas tais operações são muito comuns.

Para este exemplo com OHLCV, a fim de torná-lo mais adequado em termos de eficiência de memória e tempo, provavelmente seria mais interessante agrupar todos os valores em uma única matriz 2D ou mesmo 1D:

double TOHLCV[][6];

Isso é possível porque todos os valores dos tipos (double, datetime, long) têm o mesmo tamanho de 8 bytes e podem ser convertidos uns para os outros diretamente.

 
Stanislav Korotky #:
Para este exemplo com OHLCV, a fim de torná-lo mais adequado em termos de eficiência de memória e tempo, provavelmente seria mais interessante agrupar todos os valores em uma única matriz 2D ou mesmo 1D:

Uma matriz 2D em vez de uma matriz de estruturas pode economizar um pouco de tempo de processador, mas aumentará consideravelmente o tempo que o desenvolvedor gasta no desenvolvimento e na manutenção do código. Na minha opinião pessoal. Concordo com o restante de suas afirmações.

 

https://www.mql5.com/pt/articles/17693#sec2

Vejamos um exemplo problemático:

// Abordagem ineficiente — cria novos arrays a cada tick
void OnTick()
{
   // Isso cria uma nova matriz a cada tick
   double prices[];
   ArrayResize(prices, 1000);
   
   // Preencher a matriz com os dados de preços
   for(int i = 0; i < 1000; i++)
   {
      prices[i] = iClose(_Symbol, PERIOD_M1, i);
   }
   
   // Processar os dados...
   
   // A matriz acabará sendo recolhida pelo gerenciador de memória, mas isso
   // gera desperdício desnecessário de memória
}

Uma abordagem mais eficiente seria:

// Variável de membro da classe - criada uma única vez
double prices[];

void OnTick()
{
   // Reutilizar a matriz existente
   for(int i = 0; i < 1000; i++)
   {
      prices[i] = iClose(_Symbol, PERIOD_M1, i);
   }
   
   // Processar os dados...
}

Stanislav Korotky #:

O artigo parece bastante discutível (apenas alguns pontos).

Qual é a classe que você mencionou aqui?

Pela presença do manipulador OnTick e pela forma como a matriz é acessada, fica implícito que você adicionou a matriz de preços ao escopo global, o que é uma má ideia (devido à poluição do namespace, se a matriz for necessária apenas no escopo do manipulador). Provavelmente seria mais apropriado manter o código inicial do mesmo exemplo, mas tornar a matriz estática; dessa forma, todos veriam claramente a diferença:

Pelo que entendi, esse exemplo (que citei acima) é, grosso modo, pseudocódigo. Ou seja, o autor não presta atenção ao seguinte (para se concentrar no que exatamente está falando, eu acho):

  • A julgar pela condição do loop, o tamanho da matriz é conhecido em tempo de compilação, mas, mesmo assim, a matriz é dinâmica.
  • Embora a matriz seja dinâmica, ArrayResize não foi chamado no código que demonstra a abordagem eficiente.
  • Em termos de eficiência, suspeito que seria melhor substituir todo o loop a seguir por uma única chamada a CopySeries:

   // Reutilizar a matriz existente
   for(int i = 0; i < 1000; i++)
   {
      prices[i] = iClose(_Symbol, PERIOD_M1, i);
   }
 
Vladislav Boyko #:
Em termos de eficiência, acho que seria melhor substituir todo o loop a seguir por uma única chamada a CopySeries:

Corrija-me se eu estiver errado, mas, pelo que me lembro, cada chamada iClose contém uma chamada CopySeries por baixo do capô.

 

Este artigo apresenta um conteúdo perspicaz e instigante para discussão.

A apresentação técnica é clara e bem explicada, facilitando a compreensão do leitor e mantendo seu interesse.

Muito obrigado.

 

Em artigos como este, são necessários testes comparativos motivadores, que demonstrem de fato a eficácia das abordagens propostas.

A tradução está um pouco confusa; sem analisar o código, não é fácil compreendê-la.