Блог им. Dzam

Производительность роботов на C# (NinjaTrader).

    • 23 марта 2017, 00:26
    • |
    • Dzam
  • Еще

Производительность роботов на C# (NinjaTrader).

 Производительность роботов на C# (NinjaTrader).
Перед тем, как использовать в своем роботе переменные типа Dictionary или List, если у вас производится частое обращение к ним, обязательно проведите анализ на производительность. Вот мой кусочек анализа.

Для примера описываем переменные:

private List<KeyValuePair<int, string>> listArray;
private Dictionary<int, string> dictArray;

По сути будем иметь набор связок Integer и String. Содержание в данном случае не особо важно. Важно то, что это содержание одинаково в обеих переменных.
А теперь просто заполним эти переменные одинаковыми записями:

// Переменные для замера времени выполнения
sw1 = new Stopwatch();
sw2 = new Stopwatch();

// Инициализация переменных
listArray = new List<KeyValuePair<int, string>>();
dictArray = new Dictionary<int, string>();

// Стартуем замер производительности
sw1.Start();
for (int i = 0; i < 1000000; i++)
{
//Добавляем переменную в массив
    listArray.Add(new KeyValuePair<int, string>(i, "test"));
}

// Останавливаем замер производительности
sw1.Stop();
// Выводим результат
Print("List: " + sw1.ElapsedMilliseconds);
// Очищаем список
listArray.Clear();


// Стартуем второй счетчик производительности
sw2.Start();
// Запускаем второй цикл
for (int i = 0; i < 1000000; i++)
{
    dictArray.Add(i, "test");
}

// Останавливаем счетчик
sw2.Stop();
// Выводим результат
Print("Dictionary: " + sw2.ElapsedMilliseconds);

А вот результат:

List: 33
Dictionary: 73

Я исследовал данный вопрос для своей задачи. Перед применением, обратите внимание на плюсы и минусы относительно ваших целей. Например я читал, что Dictionary медленно добавляет записи, но зато довольно быстро обрабатывает их (поиск, например).

P.S. Также можно ускорить процесс, если во время инициализации определить размер массивов.  Код и замеры не буду писать. Отмечу только, что скорость значительно выше. Проводите эксперименты!!!

Оригинал.
★14
91 комментарий
А можно узнать конкретную аудиторию этого поста?
HFT?
Или ещё что-то?
Счастливый Лузер, алгоритмисты в принципе. Т.к. это актуально и для бэк-тестинга, и для Монте-Карло и т.п.
Бобровский Дмитрий, то есть для просот теста на часовиках?
И для теста будущего по случайным?
Странный совет!
Код не разбирал, потому что изначально прочитал пост и в комментариях указал про ОБЯЗАТЕЛЬНОСТЬ.
Счастливый Лузер, если стратегия вычислительно сложна и пространство параметров велико, то бэк-тестинг на неоптимальном коде может занимать на порядок больше времени, например. Ясно дело, что для тестирования SMA(N1) > SMA(N2) такие тонкости не важны.
Бобровский Дмитрий, а по поводу простого, что массив надо сразу определять?
Пойдем с основ?
Есть 2,5 вида ограничений:
1. ФП не позволит
2. Программа (и т.п.) не позволит.

Что далее?

Далее уже тут начинаешь изначально писать то, что будет позволять.

Плохо, когда программист не знает того, какие ограничения в платформе/языке/приложении/программе (уж в железе он знает).


Счастливый Лузер, коллекции никто изначально не определяет. Для List'а если задать изначальный размер, то это ускорит изначальную работу с List'ом до момента потребности в выделении памяти под N+1 элемент. С Dictionary сложнее — добавление элемента требует балансировки дерева. Зато поиск за O(1) вроде. У List'а это O(log N).
Тут скорее балансировка между максимальной масштабируемостью (абстракции, шаблоны, коллекции) и скоростью. Если бы автор топика решил добавить в обзор ещё и работу с простым массивом, то очень бы удивился росту скорости по сравнению с тем же List'ом.
Бобровский Дмитрий, я для остальных спрашивал наиболее понятным языком.
Как минимум про целевую аудиторию, плюс про то, что массив задать, о котором сказано в самом конце.

Если это для практиков под нинзю — то есть куча ресурсов, где это просто необсуждаемо и аксиома.
Если это для сЛаба — то надо бы и уточнить/ответить.
Счастливый Лузер, так я и написал, что ЦА — алготрейдеры, особенно начинающие. Более того, это относится и к другим проектам а-ля Wealth-Lab, TSLab, QuantConnect или S# — зачастую правильно выбранная объектная модель позволит существенно сократить время на бэк-тесты стратегий.
Бобровский Дмитрий, давайте проще:
ПОСТ относится к бэктесту!

об этом нет ни одного упоминания напрямую!
Счастливый Лузер, Пост относится не только к бэктесту. Если вы торгуете минутку, а вам необходимо анализировать тиковые данные для входа. То можно собирать тики в List и в нужный момент их использовать (либо обратиться к тиковому графику, что не всегда удобно, особенно если вам нужны не все тики, а только какая то часть). И когда вы будете писать эти самые тики в List (Dictionary) то при большом потоке данных, можете повесить систему, если сделать не верный выбор.
avatar
Dzam, в этом случае моя реакция на решение будет уже намного больше того, что анализируется. смысл пропадает.
Бобровский Дмитрий, Спасибо. Очень точный ответ.
avatar
Счастливый Лузер, Я для нинзи пишу уже несколько лет. Но пока не столкнулся с потерей производительности — на задумывался над этим вопросом. У всех свои аксиомы.
avatar
Dzam, сови аксиомы и свои «границы/требования».
Я об этом изначально и писал.
Железо/софт/разум.
Бобровский Дмитрий, раза в полтора быстрее…
Сергей Гаврилов, Вообще лучше тут посмотреть. Довольно занимательно. https://github.com/ukushu/DataStructuresTests
Бобровский Дмитрий, 
1. Все зависит от конкретной задачи. Если все время писать в коллекцию и редко читать, то List<T>, если все же читать, то конечно Dictionary<T>
2. У List<T> нет реализации ConcurrentList<T>, во всяком  случае в 4.0,  а ConcurrentDictionary<T> — существует.
3. С Arrays при неаккуратном обращении  возникает boxing/unboxing
avatar
_sg_, а ConcurrentBag как аналог потокобезопасного List? Насчёт выбора типа коллекции не соглашусь. Основное преимущество Dictionary — O(1) в операциях поиска. Рандомизированный доступ к конкретному элементу для List O(1) тоже, если мне память не изменяет. А boxing/unboxing явление довольно частое и скорее относится к категории «прямых рук», нежели к выбору коллекции, имхо.
Бобровский Дмитрий, Да. Я знаю про простой массив. :) Тут удивительного ничего нет. Я специально сравнил два подобных списка. Где можно искать по ключам. Добавлять записи через Add и так далее.
avatar

Dzam, а как же HashSet или LinkedList? ;-) 

Бобровский Дмитрий, А еще HashTable, Stack… Этих оберток очень много. Направление есть, а дальше можно исследовать в свое удовольствие. :)
avatar
Dzam, у хэштэйбла есть все шансы обойти. Буквально сегодня тоже тестировал его разновидность.
avatar
Счастливый Лузер, Массив вида double[] нужно определять сразу. А вот списки типа Queue, Dictionary, List и так далее можно не определять. В этом одно из их преимуществ. Я их использую, когда заранее не известен конечный размер. 
avatar
Бобровский Дмитрий, изначально Вы можете только предположить «сложность» и «емкость» вычислений исключительно эмпирикой.
Зная заранее данные железа+софта+мануал, Вы изначально построите код так, чтобы избежать ограничений из Вашей эмпирики.

Занятно, что Вы предупреждаете остальных.
Однако, нет ответа на мой начальный вопрос: какова целевая аудитория, что даже Т.М, отметку в плюс поставил, хотя в своей жизни просто формулу параболы с дискриминантом не запрогил!??????7
(ХеллоВорлд н даже и не писал в коде — это слишком банально для гения)
Счастливый Лузер, вы прекрасно можете пройтись профайлером по Вашему коду и определить затраты времени. А дальше выбирать между объектными моделями с разным уровнем абстракции и скоростью.
Бобровский Дмитрий, ещё раз… вот я не профи в нинзе, даже в C#/
Более того, я вообще не программист по образования.
И ещё более мне пофиг, но я тут откликнулся, чтобы пост автора стал более информативным.

Для того и задавал волпросы в комментариях, кое-где своё писал.

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

А не просто начать, а потом столкнуться.

Программировать я начал для себя примерно 28 лет назад!

Потому и первый же мой коммент в теме сознательный.
Счастливый Лузер, Это можно применять не только для теста. Данный пример применим везде, где идет большое количество добавления данных в список.
avatar
Dzam, возможно, но не обязательно.
Бобровский Дмитрий, если нужна супер-скорость, то это не актуально.., так как в этом случае о словарях вообще забыть надо… Только массивы…
Сергей Гаврилов, да.
Сергей Гаврилов, хоть как-то, не нытьем — так каканьем, суть поста проявляется.
Хотя проще было сразу оговориться, что про «бэктесты» и моделирование идёт речь в посте!!!!!!!!!!1
Сергей Гаврилов, Для моей задачи переход с Dictionary на List дал желаемый результат без заморочек со массивами. А в остальном вы правы.
avatar
Счастливый Лузер, Все кто пишет роботов. Ведь не только для HTF можно автоматизировать торговлю.
avatar
 P.S. Также можно ускорить процесс, если во время инициализации определить размер массивов.


В иных случаях не является ли это ОБЯЗАТЕЛЬНЫМ УСЛОВИЕМ при написании программы?
Счастливый Лузер, О каких иных случаях идет речь? Списки можно использовать без определения конечного размера. В основном их и используют, когда размер заранее не известен. Чтобы избежать ручного перераспределения.
avatar
Dzam, я цитировал в общем… для любого языка/среды.
Ранее было от меня про то, что нужно знать ограничения. Если их нет (только ФП) — тонет вопросов.
Есть еще момент. Желательно учитывать какие операции будут использоваться чаще. Чтение/доступ либо модификация списка. Внутри этих контейнеров могут быть реализованы разные структуры и алгоритмы. И какие то заточены для одного, а какие то для другого
avatar
Андрей К, Я поэтому написал, что в каждой конкретной задачи нужно выбирать. В комментах тоже уже об этом писали. У List одни плюсы и минусы, у Dictionary другие. А у простых массивов свои. :)
avatar
Прошу прощение, вы про это и написали
avatar
В свое время я тоже исследовал скорость разных списков, словарей… А ребята из крупной прогерской конторе, которые занимались промышленным программированием, сказали, оно конечно все правильно, но не занимайся херней.., профайлер покажет «бутылочные горлышко» и оно будет совсем в другом месте..
 С тех пор так и живем, если у Вас в коллекции 100-1000 да даже 10000 элементов, то и заморачиваться не стоит, ничего Вы не выиграете,  используйте то, что удобно…   
Сергей Гаврилов, У меня список был из 20 млн записей. Поэтому пришлось поискать. :)
avatar
Dzam, а что тут искать, в этом случае только массив… и ни каких классов внутри массива, только структуры..., только for.., оптимизация внутри for..., ну и тонкий тюнинг, например, веcь double меняем на integer, дату тоже на integer можно поменять..
Кстати по моим тестам замена класса на структуру в таких задачах скорость больше чем в два раза увеличивала…
Да еще ищем места, где принудительно мусор собираем..., например если 20 млн… с диска грузанули, то временный список чистим, а потом гарбаж…
Сергей Гаврилов, У меня хватило перевода на List, все работает в пределах поставленной задачи. Как double или дату поменять на integer? Хранить ссылки?
avatar
Не понял за счет чего Dictionary «довольно быстро обрабатывает их (поиск, сортировка).». Поиск ясно, это же хеш-таблица, но как она поможет в сортировке???
avatar
ivanovr, Возможно в сортировке ошибся. Поправил пост. Спасибо.
avatar
Перед тем, как использовать в своем роботе переменные типа Dictionary или List,

я честно комментариями пытался поправить пост, nj,.s он был понятен не только для «посвещенных»,
Первый же вопрос был про целевую аудиторию.

Надо так и писать сразу про «бэктест». ПРЯМО в заголовке или первых строках поста!
Счастливый Лузер, тема интересная, а вы флудите
avatar
broker25, я не флудил!
Я разгонял тему, чтобы она  НЕ СГИНУЛА В ПУЧИНЕ г*внопостов!
Тема реальная!
Тема жизненная.
Тема узкоспециализированная.

Я своим опытом попытался помочь автору РАСШИРИТЬ и УТОЧНИТЬ.

Рад, что за сутки тема не сгинула, а даже новый вздох приобрела!
Пост просто жесть.
какое отношение реализация базовых структур данных иммет к скорости работы торгового терминала?
Тимофей ты начал копирайтеров нанимать чтобы контент генерировали из ничего?
тайтл поста не имеет ничего общего со статьей, чистый click bait
avatar
nbvehrfr, понятно что это скорее про работу дотнета но применительно к нинже вполне.
avatar
nbvehrfr, :) попробуйте разместить приведенный выше код в обработчике OnBarUpdate и вы поймете, как это связано со скоростью работы терминала. В NT7 все (роботы, терминал, индикаторы) работают в одном потоке. Висит робот — висит терминал.
avatar
Dzam, капец для того чтобы терминал не висел не нужно «держать» обработчик OnBarUpdate а реализовать в виде non blocking кода.
avatar
nbvehrfr, Приведите пример. Думаю всем будет интересно.
avatar
Dzam, один поток висит на OnBarUpdate и проксирует данные в другой поток (Worker) — через очередь или еще что есть в c#, при этом не блокируя caller, те терминал не висит. далее в Worker делаете все что душе угодно с полученными от первого потоко данными.
avatar
nbvehrfr, Пробовали так делать? Есть пример кода?
avatar
Dzam, на скале да. пример кода? 150$/h при сроке 6 месяцев, меньше — выше ставка
avatar
nbvehrfr, Вне нинзи я знаю как использовать потоки. А вот в Нинзе… Я думал у вас есть какой то секрет.
avatar
Dzam, да тут выходит просто фигня… для чего именно? не понятно.
Автор в посте не конкретизирует задачу, приводя просто тесты его. и те с оговоркой «а больше не расскажу/».
Dzam, вам предложили все рассчеты убрать в асинхронный Worker. Если простым языком. Например в такой https://professorweb.ru/my/WPF/documents_WPF/level31/31_3.php
OnBarUpdate при этом просто будет вызвать рассчет воркера и дожидаться результатов не будет, тем самым не блокируя работу. Что и есть асинхронный режим
avatar
Андрей К, Спасибо. Попробую. Было время, когда я пытался написать многопоточный скрипт для Ninjatrader 7, но ничего не вышло. На форуме писали, что Нинзя работает в одном потоке и распаралелить никак. Я пытался через Threads. Через Backgorunworker вот тут пишут, что можно.
avatar
nbvehrfr, в посте не упомянуто про терминал. как и не упомянуто про бэктест.
ИЗНАЧАЛЬНО я и просил автора конкретизировать ЦЕЛЬ поста.
Счастливый Лузер, Терминал упомянут даже в заголовке. Но дело не в терминале. Цель поста — показать что для одной и той же задачи можно использовать два разных типа переменных, но один тип будет работать в 2 раза медленнее чем другой. И не важно, оптимизация это или поиск точки входа, NinnjaTrader, Welath-Lab, TS-Lab или еще какая система.
avatar
Мы же о трейдинге говорим, тогда и смотреть нужно самые частые операции. Добавление элементов? Оч.сомневаюсь. У меня основная задача —  найти элемент списка по ключу или номеру (приходят цены и стаканы). Я и тестил этот вопрос. Dictionary логично заметно быстрее ищет по ключу, чем List. Но простой Dictionary неупорядочен. это мешает, а Ordered очень медленно (по отзывам, сам не проверял) работает. И С. Гаврилов здесь говорил о том, что foreach медленно работает. А как иначе перебирать Dictionary? По списку ключей не уверен, что быстрее. Так что, выбор скорее между List и простым массивом. Не знаю, есть ли там разница в скорости поиска ключа
avatar
broker25, если так озадачиваться, то нужно проводить масштабные тесты самому. В разных условиях, строить графики распределения результатов. На слово особо верить не стоит, каждое слово нужно перепроверить.
avatar
broker25, я от шарпа стал далек, но насколько помню, List построен внутри как бинарное дерево. А значит заточен только под определенные задачи.
Dictionary — это хэштаблица. И подходит для работы с типами данных малой величины и на не больших объемах, потому что показатель производительности всегда константен.

Так приходится ковырять каждый контейнер под свои задачи. boost вон тоже наделал контейнеров, а там большая часть для обычных задач не подходит по производительности.
avatar
Андрей К, а вы фикс так таки свой сделали? много багов неожиданных было? или все-таки перешли на quickfix? у биржи примеры и те на quickfix вроде. Я в тестовом юзаю quickfix, но скоростью не очень доволен (хотя может в алгоритме дело)
avatar
broker25, да сделал. Если бы продолжал на шарпе, то статьи бы дописал. Но поменял язык.
Если делаете фикс под срочку, пробуйте еще твайм. Он бинарный, сообщения формируются быстрее. Объем данных меньше и тд.
avatar
хм… а тема действительно интересна, вопрос где и для чего это использовать.
Учитывая что все данные вроде как сортированы по времени, то везде можно и обычный массив использовать, если память позволяет. в любом случае доступ линейный.
Другое дело соответсвие «тикер: набор данных» может вполне быть помещен в какой нить контейнер, так как врядли одновременно надо будет работать с десятками тысяч записей.
— для бэктеста время по большому не так важно, а значит можно делать как удобнее.
— для инициализации данных, для расчета чего либо перед стартом алгоритма? тут уже может быть более важно, возможно даже это самый вероятный случай когда оптимизация по записи и чтению может понадобиться.
-Для торговли, нам вроде как нужны лишь текущие данные, а значит доступ может быть прямо к обьекту структуры, ну или опять же таки через key:value если много разных тикеров.

В общем то хотелось бы действительно узнать в каких ситуациях стоит пренебречь удобством, ради выигрыша времени?
avatar
Denis, Пример: алгоритм, который оптимизирует себя перед каждым входом. В комментариях выше писал пример, когда необходимо собирать тики, чтобы потом на их основе торговать.
avatar
Dzam, это опять же таки пример инициализации перед выполнением, но почему это нельзя сделать в конвеерном режиме, т.е. данные которые мы уже посчитали остаются неизменными, нам ведь не надо прогонять их снова, просто добавляем новые.
Я скорее абстракно размышляю, не про нинзю в данном случае, я ее не пользую. У меня интерес скорее личный, для своей системы :)
avatar
Denis, Зависит от вашей задачи. Смотрите, вам нужно найти ситуацию, когда за 14 тиков (не более чем) объем растет в 2 раза. Я бы в данном случае использовал Queue для хранения 14 элементов, чтобы искать минимум и максимумы объемов для вычисления разницы.
avatar
Dzam, ну это уже специфика реалтайм расчета, тут вроде как не нужен весь набор данных же.
avatar
Denis, Как бы вы искали это в реалтайме? Опишите, пожалуйста алгоритм. Весь набор данных не нужен, но для хранения в памяти 14 тиков мы будем добавлять текущий тик в конец очереди, а первый тик удалять.
avatar
Dzam, я имею ввиду не нужно всю историю хранить, только 14 тиков.

я так понимаю вы складываете обьемы там? как вариант хранить в памяти сумму последних 14 тиков, и значение первого тика, потом при каждом апдейте берете сумму — первый тик + последний и у вас новое значение. но это так на вскидку. :)
avatar
Denis, Да. Отличный вариант. Тогда давайте поменяем условия. Нам нужен не рост объема за последние 14 тиков, а рост цены или ее падение на определенную величину. Т.е. ловим импульс.
avatar
Dzam, в таком примере нужно пробовать LINQ, может суметь обогнать при правильном построении данных.
avatar
Андрей К, У Дениса вопрос был больше не по скорости, а по ситуации. Когда необходимо применять списки.
avatar
 Еще раз повторю свою мысль… Вставьте свою прогу в профайлер и Вы узнаете о ней много интересного… И все эти заморочки с типами коллекций покажутся настолько несущественными…
Сергей Гаврилов, Расскажите о каком профайлере идет речь? Я описал историю, которая случилось при разработке робота под NinjaTrader7.
avatar
Dzam, 

Сергей Гаврилов, так то профайлером мы конечно пользуемся, коли надо что отловить.
Но там же люди вроде нинзю трейдер юзают.
avatar
Denis, а что си-шный файл из нинзи в профайлер не влезает? К процессу подключиться нельзя?
Сергей Гаврилов, Подключиться можно, но для отладки кода необходимо подключаться к уже запущенному процессу. Я не нашел способ замера производительности кода для NinjaTrade7. Если есть идеи, то можете поделиться.
avatar
Сергей Гаврилов, Да. Очень удобная штука, но в случае с NT7 не поможет. Так как для отладки приходится присасываться к уже запущенному процессу в памяти.
avatar
Dzam, у нинзи нет профилировщика вроде как =)
avatar

Андрей К, У Нинзи нет, но у Visual Studio есть. Правда в случае с Нинзей им не получается воспользоваться.

avatar
Я хз, я нинзяй не пользуюсь :), но и профайлер не панацея, лучше же сразу красиво все делать, чем потом всю архитектуру переделывать
avatar

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

В вообще, то, что описал топик-стартер в технологии программирования называется «преждевременной оптимизацией»…  

Я рад тому, что поспособствовал теме. Не сгинул топик, написанный в неудобное время, в пучину остальных постов.

Основная цель была именно эта!

Наполнить комментариями.
А можно просьбу?
Я всё же не дал вчера сгинуть посту в пучину.
Простейший алгоритм запрогить в ниньзе.
Сравнить просто с иными средами/терминалами.

Буду благодарен.

(в личку всё)

теги блога Dzam

....все тэги



UPDONW
Новый дизайн