Рациональные приемы (предложения) ускорения оптимизатора - страница 6

 
George Merts:

Не уверен, что это сильно ускорит обработку. Насколько я знаю, стандартные индикаторы - уже выполняют эту оптимизацию. Впрочем, попробовать можно.

Говорю ж... Профилировщик очень бы помог...

Конечно, ускорит. iHighest и iLowest - это не индикаторы. Хотя, оптимизация прослеживается:

 

 
Andrey Khatimlianskii:

Конечно, ускорит. iHighest и iLowest - это не индикаторы. Хотя, оптимизация прослеживается: 

Не, все что вы сказали - верно. Действительно, наибольшее время вычисляются именно переменные iHigest и iLowest.

Просто мне казалось, что там уже эта оптимизация есть. Ну, если нету - да, ее можно применить.

 
Andrey Khatimlianskii:

Хотя, оптимизация прослеживается:

Даже не оптимизация, а использование кэша.

При первом доступе к тайм-серии тратится в 2 раза больше времени.

 

В коде без использования классов я так и делал, как предложил Andrey Khatimlianskii

Но в моем советнике используется 18 стратегий, где period пересчитывается и является динамическим, т.е. зависящим от рынка. Поэтому перешел на классы. 

class Channel
  {
protected:

   void Channel_0()
     {
      h=iHigh(_Symbol,0,0);
      l=iLow(_Symbol,0,0);
      hbar = 0;
      lbar = 0;
     }

   void SetChannel(int _StartBar,int _CountBar)
     {
      if(_StartBar+_CountBar>Bars) {Channel_0();return;}

      if(_CountBar<=0) {Channel_0(); return;}

      hbar=iHighest(NULL,0,MODE_HIGH,_CountBar,_StartBar);
      if(hbar>=0) h=iHigh(NULL,0,hbar);
      else { Channel_0(); return;}

      lbar=iLowest(NULL,0,MODE_LOW,_CountBar,_StartBar);
      if(lbar>=0) l=iLow(NULL,0,lbar);
      else { Channel_0(); return;}
     }

public:
   double            h;
   double            l;
   int               hbar;
   int               lbar;

                     Channel()
     {
      Channel_0();
     }

                     Channel(int _StartBar,int _CountBar)

     {
      SetChannel(_StartBar,_CountBar);
     }

Все советники используют этот класс.  

Так вот вопрос правильно перед  class Channel объявить эти массивы ?

На мой взгляд не правильно. Т.к. на каждом тике будут браться значения последнего расчета для h и l .  А как тогда правильно это сделать ? 

Чтоб было понятней: как запомнить значения в классе рассчитанные на предыдущем тике и при этом не потерять в производительности? Насколько я понимаю, можно все скидывать в файл, но думаю выигрыша это особого не даст. 

 

Dimeon, как я понял, Андрей имел ввиду совсем другое.

В исходном коде - каждый раз берется максимальное и минимальное значение ряда баров.

А можно сделать иначе.

При каждом тике - глядим, новый ли это бар, или нет.

Если это бар старый - то только контролируем, не выше ли он максимума (не ниже минимума), и корректируем максимум или минимум.

Если это бар новый - то смотрим еще и на тот бар, что сейчас стал неактуален, и запрашиваем пересчет максимума или минимума только если он равен максимуму (минимуму).

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


Этот алгоритм можно использовать и без классов, и с классом.

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

 
George Merts:

Dimeon, как я понял, Андрей имел ввиду совсем другое.

В исходном коде - каждый раз берется максимальное и минимальное значение ряда баров.

А можно сделать иначе.

При каждом тике - глядим, новый ли это бар, или нет.

Если это бар старый - то только контролируем, не выше ли он максимума (не ниже минимума), и корректируем максимум или минимум.

Если это бар новый - то смотрим еще и на тот бар, что сейчас стал неактуален, и запрашиваем пересчет максимума или минимума только если он равен максимуму (минимуму).

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


Этот алгоритм можно использовать и без классов, и с классом.

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

Я же написал, что использовал этот алгоритм. А вот как передать данные в класс по считанные на предыдущем тике ? 
 
Dmitiry Ananiev:

Чтоб было понятней: как запомнить значения в классе рассчитанные на предыдущем тике и при этом не потерять в производительности? Насколько я понимаю, можно все скидывать в файл, но думаю выигрыша это особого не даст. 

Массивы должны быть в классе, как и все рабочие переменные.

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

 

Dmitiry Ananiev:
Я же написал, что использовал этот алгоритм. А вот как передать данные в класс по считанные на предыдущем тике ? 

Начните с простого алгоритма без ООП, будет понятнее и проще. 

 

Dimeon, я бы оптимизацию в рамках твоего класса сделал бы следующим образом.

1. Вынес бы из описания класса все функции. Лучше вобще в отдельный файл (я вынес их в том же файле).  

2. Перенес бы все переменные в защищенную секцию, а для доступа к ним использовал бы функции типа GetHighBound() (не делал, это затронет много другого кода, хотя и нарушает принципы ООП);

3. Вынес бы конструктор из защищенной секции в паблик-секцию. Вобще, все интерфейсные функции - должны быть паблик. Защищенными должны быть только функции, которые не должны использоваться внешними пользователями (не делал, это больше относится к принципам ООП).

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

5. Ввел функцию SetChannelOptimized() - такую же, как и SetChannel(), но с контролем необходимости вызовов iHigest и iLowest.

6. Также ввел защищенные функции установки канала для появления нового бара и для непоявления, а также функцию определения нового бара. Все эти функции начинаю с символа подчеркивания, так я обозначаю фунции, которые используют неявные соглашения, и должны использоваться пользователями класса аккуратно.

7. Функцию IsNewBar() я сделал так, как ты мне предложил, то есть по сравнению с величиной Bars. Однако, на мой взгляд - это опасная практика. Если число баров на чарте ограничено, то при появлении нового бара - может исчезнуть стары, и тогда эта функция вернет результат, что нового бара нет, а на самом деле - он есть. Лично я предпочитаю эту функцию строить через тиковый объем.

8. На нотацию я бы обращал бы больше внимания и комментов побольше бы вставлял. Оно никаких проблем, когда класс такой небольшой, но когда в эксперте используется хотя бы десяток классов - уже правильная нотация - очень помогает избегать ошибок, да и понимание облегчает.


// Канал для Димеона

class Channel
{
public:
   double            h;       // Верхняя граница канала
   double            l;       // Нижняя граница канала
   int               hbar;    // Индекс верхнего значения канала
   int               lbar;    // Индекс нижнего значения канала


protected:

   int m_iStartBar;
   int m_iNumOfBars;
  
   int m_iTotalBars;

   // Функция пересчитывает канал для случая, когда новых баров нет
   void _SetShannelInOldBar();

   // Функция пересчитывает канал для случая, когда новый бар есть.
   void _SetShannelAfterNewBar();
  
   // Функция возвращает true, если появился новый бар.
   // Кроме того, запоминает новое значение количества баров
   bool _IsNewBar();
  


// Конструктор по умолчанию
// Устанавливает канал по диапазону текущего бара
Channel();

// Конструктор с параметрами
// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам.
Channel(int iStartBar,int iNumOfBars);

// Функция устанавливает канал по диапазону текущего бара.
void Channel_0();

// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам.
void SetChannel(int iStartBar,int iNumOfBars);

// Устанавливает канал, начиная с бара iStartBar по iNumOfBars ,барам c буфферированием.
void SetChannelOptimized(int iStartBar,int iNumOfBars);
}; // Конец объявления класса    


// Функции-члены класса


Channel::Channel()
{
   Channel_0();
  
   m_iTotalBars = Bars;
};

Channel::Channel(int _StartBar,int _CountBar)
{
   SetChannel(_StartBar,_CountBar);

   m_iTotalBars = Bars;
};


void Channel::Channel_0()
{
   h=iHigh(_Symbol,0,0);
   l=iLow(_Symbol,0,0);
     
   hbar = 0;
   lbar = 0;
  
   m_iStartBar = 0;
   m_iNumOfBars = 1;
};

void Channel::SetChannel(int _StartBar,int _CountBar)
{
   // Если предложенный канал выходит за границу данных баров -
   // устанавливаем канал по нулевому бару
   if(_StartBar+_CountBar>Bars)
      {
      Channel_0();
      return;
      };
  
   // Если длина канала слишком маленькая -
   // устанавливаем канал по нулевому бару
   if(_CountBar<=0)
      {
      Channel_0();
      return;
      };

   // Устанавливаем индекс верхнего бара
   hbar=iHighest(NULL,0,MODE_HIGH,_CountBar,_StartBar);
  
   // Если бар найден, запоминаем его верхнее значение, иначе - устанавливаем канал по нулевому бару.
   if(hbar>=0)
      h=iHigh(NULL,0,hbar);
   else
      {
      Channel_0();
      return;
      };

   // Устанавливаем индекс нижнего бара
   lbar=iLowest(NULL,0,MODE_LOW,_CountBar,_StartBar);
  
   // Если бар найден, запоминаем его нижнее значение, иначе - устанавливаем канал по нулевому бару.
   if(lbar>=0)
      l=iLow(NULL,0,lbar);
   else
      {
      Channel_0();
      return;
      };
     
   m_iStartBar = _StartBar;
   m_iNumOfBars = _CountBar;
     
};

void Channel::SetChannelOptimized(int iStartBar,int iNumOfBars)
{
   // Если длина канала или его стартовые индекс отличается - пересчитываем в любом случае.
   if(iNumOfBars != m_iNumOfBars || iStartBar != m_iStartBar)
      {
      SetChannel(iStartBar,iNumOfBars);
      return;
      };
  
   // Пересчитаем канал, в зависимости от того, пришел ли новый бар
   if(_IsNewBar())
      _SetShannelAfterNewBar();
   else  
      _SetShannelInOldBar();

};

void Channel::_SetShannelInOldBar()
{
   // Пересчитаем канал в старом баре
   double dCurrentHigh = iHigh(NULL,0,hbar);
   double dCurrentLow = iLow(NULL,0,lbar);
  
   if(dCurrentHigh > h)
      {
      h = dCurrentHigh;
      hbar = 0;
      };
     
   if(dCurrentLow < l)
      {
      l = dCurrentLow;
      lbar = 0;
      };
};

void Channel::_SetShannelAfterNewBar()
{
   // Пересчитаем канал в новом баре
  
   // Проверим, возможно, устаревшее значение - было экстремумом,
   // в этом случае - надо пересчитать канал полностью
   if(hbar+1 == m_iNumOfBars || lbar+1 == m_iNumOfBars)
      {
      SetChannel(m_iStartBar,m_iNumOfBars);
      return;
      };
  
   // Устарвешее значение экстремумом не было.
   // Следовательно, надо проверить только последний тик.
   _SetShannelInOldBar();
};

bool Channel::_IsNewBar()
{
   if(m_iTotalBars == Bars)
      return(false);

   m_iTotalBars = Bars;

   return(true);
};

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