Алгоритмическая или реальная оптимизация?

16 апреля 2024, 00:24
fxsaber
3
113

Для ускорения оптимизации ТС делают следующее

  1. Увеличивают количество параллельных вычислительных потоков.
  2. Пробуют разные компиляторы.
  3. Переписывают код под особенности железа (OpenCL, GPU и т.д.).
  4. Пробуют разные алгоритмы оптимизации.
  5. Уменьшают количество входных данных (цены, календарь и т.д.).
  6. Заменяют внутренние алгоритмы на более оптимальные по вычислительным ресурсам.

Последний пункт называют алгоритмической оптимизацией.


Реальная оптимизация.

А может ли реальная (вычислительная) оптимизация ускорить оптимизацию? Звучит, как масло масленное.

Ниже приведу пример, который, возможно, кого-то натолкнет на полезные идеи ускорения расчетов в своих ТС.


Пример.

Хотелось привести не гипотетический, а реальный пример, но при этом лаконичный. И тут случай подвернулся.


Разбирался с особенностями DST/GMT-смещений в разных источниках котировок и календаря. Там многое завязано на первом/втором/последнем воскресенье месяца. Поэтому ядром подобных вычислений является расчет времени начала месяца. Вот эту функцию и попробуем ускорить.

datetime GetMonthTime( const int Year, const int Month )
{
  const MqlDateTime time = {Year, Month, 1};
  
  return(StructToTime(time));
}


Казалось бы, что тут ускорять? А главное - как?!


Оптимизация - кто кому служит?

Чем меньше математических действий, тем быстрее работает алгоритм. Поэтому немного включив интуицию, напишем общий вид функции, который хотим получить.

#define DAY (24 * 3600)

datetime GetMonthTime2( const int Year, const int Month )
{
  // Количество дней от начала года до начала месяца: январь - 0, февраль - 31, март - 59, ...
  static const double Months[] = {0, 0, 31, 59, 90, 120, 151, 181, 212 ,243, 273, 304, 334};
  
  return((datetime)(((Year - 1970) * inKoef1 + Months[Month] * inKoef2) * DAY) / DAY * DAY);
}

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


Коэффициенты.

А вот подобрать эти коэффициенты способна стандартная оптимизация. Поэтому пишем ФФ (фитнесс функцию) и натравливаем оптимизатор.

input double inKoef1 = 365;
input double inKoef2 = 1;

sinput int inYearFrom = 1970;
sinput int inYearTo = 2099; 
input int inMonth = 1;

double OnTester()
{
  double Sum = 0;
  
  for (int i = inYearFrom; i <= inYearTo; i++)
    Sum -= (double)MathAbs(GetMonthTime(i, inMonth) - GetMonthTime2(i, inMonth));

  return(Sum);
}


Для наглядности, в качестве оптимизатора выступит многоядерный MT5-Тестер в режиме математических вычислений.



Запускаем и мгновенно получаем результат.

Нулевая ошибка (полное совпадение с тем, что надо получить) и комбинации соответствующих коэффициентов.


Делаем так для каждого месяца. И в итоге получаем искомую функцию.

// Работает до конца XXI-века.
datetime GetMonthTime3( const int Year, const int Month )
{
  static const double Months[] = {0, 0.45, 31 * 1.01, 59 * 1.01, 90 * 1.007, 120 * 1.005, 151 * 1.004, 181 * 1.004,
                                  212 * 1.003, 243 * 1.003, 273 * 1.002, 304 * 1.002, 334 * 1.002};
  
  return((datetime)(((Year - 1970) * 365.25 + Months[Month]) * DAY) / DAY * DAY);
}


Ускорение.

Выглядит некрасиво, но работает в 10-20 раз быстрее исходной!

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

Ссылка на блог автора.

Файлы:
Поделитесь с друзьями: