Новая версия платформы MetaTrader 5 build 5260: улучшения в Algo Forge, расширение OpenBLAS и новые правила наследования в MQL5 - страница 5

 
fxsaber #:

b5274, разное поведение оптимизатора компилятора для статических и динамических массивов.


В ходе обсуждения выяснилось, что статические массивы работают быстрее динамических.

Немного изменил старый код и получил неожиданный результат.

Статический будто был вырезан компилятором, но результат правильный.

Строка для поиска: Uluchshenie 137.

Надо бы уточнить, что имеется в виду под словом "статический". К сожалению, в MQL5 этот термин используется и для модификатора static и для массивов с явно указанным размером (поэтому я предлагал называть их фиксированными для исключения разночтений).

Полагаю в данном примере получен 0 длительности именно из-за модификатора static, а не фиксированного размера. Для чистоты эксперимента static нужно убрать.

 
Stanislav Korotky #:

Для чистоты эксперимента static нужно убрать.

Если без static, то это local, у которого 2 MB ограничение.

 
fxsaber #:

Если без static, то это local, у которого 2 MB ограничение.

Вынесите массив на глобальный уровень без static

 
Rorschach #:

Вынесите массив на глобальный уровень без static

Чтобы совсем без доп. трактовок.

#define BENCH(A)                                                             \
  {                                                                          \
    const ulong StartTime = GetMicrosecondCount();                           \
    int Res = A;                                                             \
    Print(#A + ":" + (string)(GetMicrosecondCount() - StartTime) + " mcs."); \
    Print(Res);                                                              \
  } 
  
struct A
{
  int i;
  double d;
  bool b;

  void Init()
  {
    this.i = 1;
    this.d = 1;
    this.b = 1;
  }
};

#define SIZE 10000000

A Array1[SIZE];  

A Array2[];
int Init = ArrayResize(Array2, SIZE);   

int Bench1()
{
  int Res = 0;
  
  for (uint i = SIZE; (bool)i--;)
  {
    Array1[i].Init();
      
    Res += (int)(Array1[i].i + Array1[i].d + Array1[i].b);
  }
  
  return(Res);
}

int Bench2( A &Array[] )
{
  int Res = 0;
  
  for (uint i = SIZE; (bool)i--;)
  {
    Array[i].Init();
      
    Res += (int)(Array[i].i + Array[i].d + Array[i].b);
  }
  
  return(Res);
}

void OnStart()
{
  for (int i = 0; i < 5; i++)
  {
    BENCH(Bench1());       // 0 - если строка ниже закомментирована, иначе - 58000.
//    BENCH(Bench2(Array1)); // Влияет на результат выше.
    BENCH(Bench2(Array2)); // 28000.
  }
}
 
fxsaber # :

b5274, разное поведение оптимизатора компилятора для статических и динамических массивов.


В ходе обсуждения выяснилось , что статические массивы работают быстрее динамических.

Немного изменил старый код и получил неожиданный результат.

Статический будто был вырезан компилятором, но результат правильный.

Строка для поиска : Uluchshenie 137.

Что неожиданного? Все это делается во время компиляции статической версии.
 
Alain Verleyen #:
Что неожиданного? Все это делается во время компиляции статической версии.

Это не так.

Дает все равно гораздо меньшее время, чем для динамического массива.

Bench1():7942 mcs.
-3
Bench2(Array2):26111 mcs.
-3
 

Между статическими и динамическими массивами в managed (и даже в C++) языках огромная пропасть.

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

В динамическом массиве компилятор не может делать жестких выводов про неиспользуемость и скорее всего пишет данные в массив.


В общем, тест некорректный. В одном случае вырожденный случай сверхоптимизации на фоне констант и неиспользования результатов "якобы записи".

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

Незнание методик жесткой оптимизации - поголовная проблема большинства коротких синтетических тестов.

Берешь такой синтетический тест, переносишь в реальный кейс пусть даже с добавлением толики реальности в виде 1-2 строк и все разваливается. А уж если десяток, да с вызовами других серьезных(не фейковых синтетических) функций, то сразу понимаешь, что сам себя обманывал.

ps: я оптимизацией кода занимаюсь уже больше 30 лет, и чем дальше - тем больше

 
Renat Fatkhullin #:

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

Про этот тест сразу было сказано.

Запускаете выложенные там скрипты и убеждаетесь.

 

Свежая статья на тему влияния динамического выделения памяти (а это динамические (не статические) массивы - ArrayResize, объекты - new/delete и виртуальные функции) на лаги в приложениях. Оказалось, что в долгоработающих нагруженных сетевых программах происходят серьезные лаги, включая дефрагментацию памяти и ее нехватку.

Рецепт избавления от этих лагов - отказаться от динамического в пользу статического. Может, это и делается в торговых платформах.


Интересно, можно ли заметить ускорение при отказе от виртуальных функций...

 
fxsaber #:

Интересно, можно ли заметить ускорение при отказе от виртуальных функций...

В статье вроде есть пара рецептов по замене виртуальных функций - можно при желании сделать эквивалентные тесты и замеры.