Секреты TSLab | Торговые роботы | События
29 Июн

Куда девается память при оптимизации скриптов?

Приветствую тебя, друг мой. Если ты читаешь данную статью, то слово TSLab тебе не кажется странным и незнакомым. Ну, а если это так, то ты обязательно пробовал использовать оптимизацию в данной программе. Нередко звучат вопросы: “А почему оптимизация поедает столько памяти?”,  “Куда ей столько?”, “А почему у меня оптимизация ЕМА потребляла максимум 3гб, а данный индикатор потребляет 8гб? Он плохо написан?”. Возможно, и ты задавался таким вопросом. И вот, я решил ответить на все вопросы одной статьей, которая прояснит все детали работы оптимизации  и потребления памяти в процессе оной.

Куда девается память?

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

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

Куда девается память при оптимизации скриптов?Рисунок 1. Скрипт с 2 кэшируемыми блоками.

Сначала будет закэширован блок Закрытие, потом StDev, а потом блок SMA. В итоге получает целых 3 результата в кэшэ. Учитывать блок Закрытие мы не будем, так как выход данного блока не изменяется и памяти потреблять будет всегда мало. В общем, чем больше разных блоков в вашем скрипте, тем больше кэша они будут занимать. Зависимость тут вполне линейная. Давайте заглянем в *.cs файл,  получающийся после запуска данного скрипта визуального редактора.

            // Initialize 'StDev1' item
            this.StDev1_h.Context = context;
            this.StDev1_h.Period = this.StDev1_Period;
            // Make 'StDev1' item data
            System.Collections.Generic.IList<double> StDev1 = context.GetData("StDev1", new string[] {
                this.StDev1_h.Period.ToString(), 
                "Источник1"
            }, delegate {
                return this.StDev1_h.Execute(Закрытие1);

            });
            // Initialize 'SMA1' item
            this.SMA1_h.Context = context;
            this.SMA1_h.Period = this.SMA1_Period;
            // Make 'SMA1' item data
            System.Collections.Generic.IList<double> SMA1 = context.GetData("SMA1", new string[] {
                this.StDev1_h.Period.ToString(), 
                this.SMA1_h.Period.ToString(), 
                "Источник1"
            }, delegate {
                return this.SMA1_h.Execute(StDev1);

            });

Видим 2 блока кода отвечающих за кэширование данных. Все как я и говорил выше. 2 блока = в 2 раза больше места нужно в кэшэ.

 

Сколько же памяти нужно?

одно число

Давайте попробуем посчитать сколько памяти сожрет оптимизация в процессе своей работы. Для этого необходимо вспомнить базовые основы программирования, а именно, сколько памяти потребляет тот или иной тип переменной. Рекомендую почитать статью про типы данных если вы плохо понимаете о чем речь. Итак, кэшировать можно числа double, int и bool величины, это ясно из существующих методов GetData. Чаще всего, нам нужно кэшировать результаты расчетов, а значит это массивы вещественных чисел, то есть массивы double. В некоторых случаях индикатор выдает целые числа (что могло бы позволить сэкономить память), НО в визуальном редакторе все числа переводятся в double и задействовать int получится только в TSLab API. Таким образом придется сосредоточиться только на double, самом распространенном и самом прожорливом варианте. Одно число типа double, хочешь не хочешь, отжимает у нас 8 байт памяти. Одно число int отнимает всего 4 байта памяти, что в 2 раза меньше.

массив чисел

С одним числом разобрались, теперь нужно разобраться с массивом чисел. Сколько будет занимать массив чисел? Если не выпендриваться, то можно грубо предположить что массив займет памяти кратно числу ячеек в массиве. Для массива длиной 1000 элементов, памяти нужно 8000 байт.

список чисел

Ладно, с массивом то понятно, а сколько занимает список?? Вот тут более интересно. Как я рассказываю на своих лекциях, внутри списка находится все тот же массив. Для того чтобы иметь возможность добавлять новые элементы в список, массив создается несколько большего размера, так сказать с запасом, поэтому памяти список, почти всегда, занимает больше чем имеет элементов. На сколько больше, не всегда можно сказать, но больше. Иногда это может быть 30% текущей длины. В итоге, список отжимает у нас памяти несправедливо много, и мы даже не может заранее знать сколько.

Вообще, зачем нам список если у нас есть массив? Да вот в том и проблема, что мы не можем знать что нам выдает индикатор на выходе себя. Возможно это будет список, а возможно это будет массив. Все блоки визуального редактора на выходе выдают IList<T>, то есть разрешен и список и массив. Следовательно, заранее нельзя сказать что именно, и стоит закладываться под списки, которые имеют несколько большие аппетиты.

проблема длинных массивов

В языке C#, да и в других языках тоже, есть проблема выделения памяти под очень длинные непрерывные структуры, к которым относятся и массивы. Если нам нужно 100 000 ячеек памяти по 8 байт, то операционная система будет искать кусок памяти ЦЕЛЬНЫЙ и НЕПРЕРЫВНЫЙ.  Если у нас сейчас программой уже зарезервировано 500 мб памяти, но нет такого цельного длинного куска, то программа запросит у ОС еще памяти и займет новую память. Чем чаще будет происходить такая ситуация, тем больше будет потреблено памяти, при том что фактически куча памяти будет свободной.

Именно этим объясняется тот факт, что большинство программ со временем начинают потреблять все больше и больше памяти. Мой любимый браузер постепенно переходит планку в 2-3 гигабайта и мне приходится его перезапускать. Такую же процедура приходится делать с программой TSLab с завидной периодичностью.

итоги

В конце концов, мы приходим к выводу что 1 результат расчета будет занимать памяти в кэшэ = число бар * 8. Если предположить что свечей у нас взято 100 000 штук (что вполне себе реальность), то один результат скушает у нас 8 * 100 000 = 800 000 байт памяти = 0.76 мб памяти.

В кэш будут попадать только уникальные результаты расчета, следовательно, если мы оптимизируем индикатор SMA на периоде от 1 до 300, то будем иметь 299 уникальных результатов и значит оптимизация займет памяти  299 * 0.78 = 227 мб. Не такая внушительная величина, но не всегда все происходит так легко и приятно как с SMA.

Проверка боем

Давайте проверим на реале все описанные выше расчеты. Большой точности у нас не получится, но в пределах допустимой погрешности мы сможем понять на сколько мы были правы или не правы. Для красоты эксперимента возьмем историю в 1 000 000 бар и элементарный скрипт

Куда девается память при оптимизации скриптов?

 

Рисунок 2. Скрипт с SMA и 1 000 000 свечками.

Запустим TSLabс нуля и оценим потребление памяти, после чего запустим оптимизацию на 300 итераций и посмотрим потребление после этого. У меня получилось 580 мб на старте, а по завершению стало

Рисунок 3. Потребление памяти ДО и ПОСЛЕ оптимизации.

Теперь бессовестно включим математику и посмотрим сколько скушала оптимизация, и вспомним о том что в одном гигабайте всегда 1024 мегабайта, а в одном мегабайте 1024 килобайта, а в килобайте всегда 1024 байта.

Под оптимизацию ушло 2.8 * 1024 =  2867.2 мб. Значит на один уникальный результат ушло 2867.2 / 300 = 9.56 мб. То есть на массив длиной 1 000 000 ячеек ушло 9.56 мб памяти. Значит на одну ячейку ушло 9.56 / 1 000 000 = 10 байт.  В итоге на одно число double у нас ушло 10 байт.

Все сходится! Бинго! Небольшая погрешность может быть связана как с тем, что на выходе кубика были списки и памяти было скушано с запасом, так и с тем что TSLab выделил память не только под кэш, но и под другие промежуточные расчета, а так же с тем что закэшировались цены закрытия инструмента (что заняло 10 мб памяти). Просто, нужно быть готовым к тому что памяти будет скушано несколько больше чем получается по нашим грубым расчетам. Но меньше не будет однозначно :).

Выводы

Конечное потребление памяти при оптимизации легко рассчитать по следующей формуле:

Память (байт) = ЧислоБар * 8 * ЧислоУникНаборовПараметров

ЧислоБар – сколько бар истории вы подали в скрипт

8 – столько займет одно число double

ЧислоУникНаборовПараметров – сколько уникальных сочетаний параметров в скрипте получается. Это придется рассчитать самому. Далеко не всегда эта цифра равна числу итераций в скрипте. Для двух скользящий с периодом от 1 до 300, число уникальных наборов будет равно всегда 299, при том что число пересчетов будет много больше. И именно за счет кэширования этих самых 299 уникальных результатов будет получен тот самый прирост скорости оптимизации, который получается при помощи использования GetData.


comments powered by HyperComments

Павел
2015-11-19 13:17:41
И что делать, если не хватает памяти? Почему tslab не может использовать жесткий диск для этого? Пусть периодически резервирует пространство на нем и освобождает оперативку...
ra81
2015-11-19 13:29:34
Дьявол всегда в деталях. Скорее всего что то вы упустили и что то отличается. Просто так само по себе не могло. Может разная битность программы? 32 или 64 это большая разница по работе с памятью. Может еще что то? Сколько у вас памяти свободно? Сколько из нее потребляет тслаб? Это все важные детали с которыми нужно работать прежде чем делать выводы.
ra81
2015-11-19 13:56:19
может проблема как раз в том что у вас другая ОС теперь? И она жрет больше памяти и тслабу остается меньше и так далее. Нужно разбираться предметно. изложенные тут расчеты в принципе верны и подтверждаются. Но всегда есть детали о которых вы можете не знать и не понимать их когда излагаете ситуацию. Я на основании того что вы пишете не могу помочь. Мало данных
Павел Строков
2015-11-22 04:56:24
спасибо за советы! Завтра заберу комп с 8ГБ и попробую оптимизацию снова. Кстати, ОС всегда 8 была:)
Павел Дуков
2015-12-02 16:35:54
спасибо
Жора
2017-01-01 07:10:35
Казино Вулкан раздают деньги сегодня http://cenforce100.ru/casino-vulkan.php
8
Сен
2017

Доверительное управление. Результаты в августе 2017 года.

Доверительное управление. Результаты в августе 2017 года. Стратегия “Опционы” принесла в августе прибыль в размере… »

6
Авг
2017

Доверительное управление. Результаты в июле 2017 года.

Доверительное управление. Результаты в июле 2017 года. В июле индекс РТС вновь колебался в достаточно узком… »

14
Июл
2017

Доверительное управление. Результаты в июне 2017 года.

Доверительное управление. Результаты в июне 2017 года. Июнь индекс РТС вновь провел преимущественно в боковых движениях, а… »

11
Июн
2017

Доверительное управление. Результаты в мае 2017 года.

Доверительное управление. Результаты в мае 2017 года. В мае “болтанка” индекса РТС продолжилась, на паре… »

7
Май
2017

Доверительное управление. Результаты в апреле 2017 года.

Доверительное управление. Результаты в апреле 2017 года. В апреле мы наблюдали очередной месяц “боковика” по… »