Новая версия платформы MetaTrader 5 build 3500: улучшения и исправления - страница 3

 
Renat Fatkhullin #:

Не должен.

Объект не может быть сконструирован/пропущен частично на основе запредельной логики минимализма использования. Иначе на каждый инстанс объекта пришлось бы строить полный граф использования каждого члена.

На простых типах типа int такое легко делается, но не для сложного/любого объекта/структуры.

Спасибо за пояснение. Ниже написал три варианта одной и той же функции и замерил скорость их выполнения. Нигде в цикле не создаются объекты.

// Какой вариант выбрать?

struct STRUCT { MqlTradeRequest Request[]; };

// Самый быстрый. Противный код.
double GetSum1( const STRUCT &Array[], const double Koef1, const double Koef2, const double Koef3 )
{
  double Sum = 0;
  
  for (int i = ArraySize(Array) - 1; i >= 0; i--)
    for (int j = ArraySize(Array[i].Request) - 1; j >= 0; j--)
      Sum += Koef1 * Array[i].Request[j].price +
             Koef2 * Array[i].Request[j].tp +
             Koef3 * Array[i].Request[j].sl;
      
  return(Sum);
}

// Жутко тормозной. Понятный код.
double GetSum2( const STRUCT &Array[], const double Koef1, const double Koef2, const double Koef3 )
{
  double Sum = 0;

  MqlTradeRequest Request;
  
  for (int i = ArraySize(Array) - 1; i >= 0; i--)
    for (int j = ArraySize(Array[i].Request) - 1; j >= 0; j--)
    {
      Request = Array[i].Request[j];
      
      Sum += Koef1 * Request.price +
             Koef2 * Request.tp +
             Koef3 * Request.sl;
    }
      
  return(Sum);
}

// Не самый быстрый. Вроде, правильный, но много текста.
double GetSum3( const STRUCT &Array[], const double Koef1, const double Koef2, const double Koef3 )
{
  double Sum = 0;
  
  for (int i = ArraySize(Array) - 1; i >= 0; i--)
    for (int j = ArraySize(Array[i].Request) - 1; j >= 0; j--)
      Sum += GetSum(Array[i].Request[j], Koef1, Koef2, Koef3);
      
  return(Sum);
}

double GetSum( const MqlTradeRequest &Request, const double &Koef1, const double &Koef2, const double &Koef3 )
{
  return(Koef1 * Request.price +
         Koef2 * Request.tp +
         Koef3 * Request.sl);
}

#include <fxsaber\Benchmark\Benchmark.mqh> // https://www.mql5.com/ru/code/31279
#define _C(A) _B(A, 1)

void OnStart()
{
  STRUCT Array[];
  
  // Инициализация.
  for (int i = ArrayResize(Array, 5e3) - 1; i >= 0; i--)
    ArrayResize(Array[i].Request, i);
    
  // Замеряем.
  Print(_C(GetSum1(Array, 1, 2, 3))); // 146684 mcs.
  Print(_C(GetSum2(Array, 1, 2, 3))); // 257444 mcs.
  Print(_C(GetSum3(Array, 1, 2, 3))); // 148417 mcs.
}

Неужели нужно писать подобие первой функции?! Второй же вариант - полная засада с производительностью.

Уважаемые форумчане, а какой вариант используете Вы?

 
fxsaber #:

Спасибо за пояснение. Ниже написал три варианта одной и той же функции и замерил скорость их выполнения. Нигде в цикле не создаются объекты.

Неужели нужно писать подобие первой функции?! Второй же вариант - полная засада с производительностью.

Уважаемые форумчане, а какой вариант используете Вы?

Все три. От случая к случаю (или от настроения?) разные.

 
Artyom Trishkin #:

Все три. От случая к случаю (или от настроения?) разные.

Вот теперь думаю, как найти в своих кодах использование второго варианта. Это же ужас, как оказалось.

 
fxsaber #:

Спасибо за пояснение. Ниже написал три варианта одной и той же функции и замерил скорость их выполнения. Нигде в цикле не создаются объекты.

Неужели нужно писать подобие первой функции?! Второй же вариант - полная засада с производительностью.

Уважаемые форумчане, а какой вариант используете Вы?

Во  втором варианте у вас же все время уходит на копирование структуры на каждой итерации цикла

 Request = Array[i].Request[j];

И по смыслу совершенно лишнее тут.

 
Azat #:

Во  втором варианте у вас же все время уходит на копирование структуры на каждой итерации цикла

Это очевидно каждому.

И по смыслу совершенно лишнее тут.

Видимо, нужно догадаться, что имели в виду.

 
fxsaber #:

Это очевидно каждому.

Странно.
Вероятно я не так понял комментарий.


Вот этот:

>>Второй же вариант - полная засада с производительностью.


Если вам очевидно, что эта операция занимает большую часть времени, потому что по сравнению с первым и третьим содержит лишнюю дорогую операцию,

то к чему тогда был вопрос про то что этот вариант выполняется дольше всего...



 
Azat #:

Если вам очевидно, что эта операция занимает большую часть времени, потому что по сравнению с первым и третьим содержит лишнюю дорогую операцию,

то к чему тогда был вопрос про то что этот вариант выполняется дольше всего...

Речь про то, какой нативный код создает компилятор из исходного. Вместо того, чтобы во втором варианте использовать "указатель" (как это делается в третьем варианте), компилятор выполняет ровно то, что прописано в тексте второго варианта.

Ну а превосходство (пусть и малое) первого варианта над третьим не может не вызывать вопросы.


ЗЫ Предполагаю, что именно причина медлительности второго варианта является сутью медленной работы с историческими таблицами.

MT5 и скорость в боевом исполнении
MT5 и скорость в боевом исполнении
  • 2022.01.20
  • www.mql5.com
MT5 - шустрая платформа. Но есть узкие горлышки, которые сводят на нет все старания быстрой торговли...
 
fxsaber #:

Речь про то, какой нативный код создает компилятор из исходного. Вместо того, чтобы во втором варианте использовать "указатель" (как это делается в третьем варианте), компилятор выполняет ровно то, что прописано в тексте второго варианта.

Ну а превосходство (пусть и малое) первого варианта над третьим не может не вызывать вопросы.

Не соглашусь.

Структура это же по сути некий user defined тип данных.

Если у нас есть оператор присваивания

Request = Array[i].Request[j];

то все происходит согласно контракта - область данных копируется из одного места в другое.

Вы же когда один int другому присваиваете, то не ждете, то в первом int будет ссылка на второй.

Здесь тоже самое.

У вас есть полноценные объекты, чтобы делать то что вы описали.

Там все будет как вы и ждете, думаю Вы и так это прекрасно знаете.

 
Azat #:

Там все будет как вы и ждете, думаю Вы и так это прекрасно знаете.

Жду оптимизации от компилятора. Здесь про это много написали сами разработчики.

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


Сам 100% частенько писал вот так (был уверен, const подсказывает компилятору, что элементарно создать оптимальный по производительности код):

double GetSum4( const STRUCT &Array[], const double Koef1, const double Koef2, const double Koef3 )
{
  double Sum = 0;

  for (int i = ArraySize(Array) - 1; i >= 0; i--)
    for (int j = ArraySize(Array[i].Request) - 1; j >= 0; j--)
    {
      const MqlTradeRequest Request = Array[i].Request[j];
      
      Sum += Koef1 * Request.price +
             Koef2 * Request.tp +
             Koef3 * Request.sl;
    }
      
  return(Sum);
}

Как оказалось, компилятор не только выполняет оператор присваивания, но еще и на каждой итерации создает объект. "Что вижу, то и пою". Вот и задаюсь вопросом, где оптимизация компилятором?

 
fxsaber #:

Жду оптимизации от компилятора. Здесь про это много написали сами разработчики.

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


Сам 100% частенько писал вот так (был уверен, const подсказывает компилятору, что элементарно создать оптимальный по производительности код):

Как оказалось, компилятор не только выполняет оператор присваивания, но еще и на каждой итерации создает объект. "Что вижу, то и пою". Вот и задаюсь вопросом, где оптимизация компилятором?

Стало интересно.

Прогнал сейчас Ваш код.


.... <тут копия трех вариантов> ...

void OnStart() {  



  STRUCT Array[];

  

  // Инициализация.

  for (int i = ArrayResize(Array, 5e3) - 1; i >= 0; i--)

    ArrayResize(Array[i].Request, i);

    

  ulong marker;

  double result;

  

  marker = GetMicrosecondCount();

  result = GetSum1(Array, 1, 2, 3);

  

  Print("1. ", DoubleToString(result), ". Time: ", 

     (GetMicrosecondCount() - marker) / 1000);

     

  marker = GetMicrosecondCount();

  result = GetSum2(Array, 1, 2, 3);

  

  Print("2. ", DoubleToString(result), ". Time: ", 

     (GetMicrosecondCount() - marker) / 1000);     

     

  marker = GetMicrosecondCount();

  result = GetSum3(Array, 1, 2, 3);

  

  Print("3. ", DoubleToString(result), ". Time: ", 

     (GetMicrosecondCount() - marker) / 1000);     

}


Результаты.


1. Компиляция без оптимизации:


2022.11.15 00:07:04.901 Test116 (RTS Splice,H1) 1. 0.00000000. Time: 755

2022.11.15 00:07:05.619 Test116 (RTS Splice,H1) 2. 0.00000000. Time: 718

2022.11.15 00:07:06.198 Test116 (RTS Splice,H1) 3. 0.00000000. Time: 579



2. Компиляция с оптимизацией:


2022.11.15 00:07:50.802 Test116 (RTS Splice,H1) 1. 0.00000000. Time: 305

2022.11.15 00:07:51.372 Test116 (RTS Splice,H1) 2. 0.00000000. Time: 569

2022.11.15 00:07:51.669 Test116 (RTS Splice,H1) 3. 0.00000000. Time: 296


Просто наблюдение..

Причина обращения: