Dzam
Dzam личный блог
23 марта 2017, 00:26

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

Производительность роботов на 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. Также можно ускорить процесс, если во время инициализации определить размер массивов.  Код и замеры не буду писать. Отмечу только, что скорость значительно выше. Проводите эксперименты!!!

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

            Что далее?

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

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


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

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

                    об этом нет ни одного упоминания напрямую!
                      • Зарабатывающий Юзер
                        24 марта 2017, 02:55
                        Dzam, в этом случае моя реакция на решение будет уже намного больше того, что анализируется. смысл пропадает.
                  • Зарабатывающий Юзер
                    24 марта 2017, 02:56
                    Dzam, сови аксиомы и свои «границы/требования».
                    Я об этом изначально и писал.
                    Железо/софт/разум.
              • Сергей Гаврилов
                23 марта 2017, 01:52
                Бобровский Дмитрий, раза в полтора быстрее…
                • Бобровский Дмитрий
                  23 марта 2017, 02:02
                  Сергей Гаврилов, Вообще лучше тут посмотреть. Довольно занимательно. https://github.com/ukushu/DataStructuresTests
              • _sg_
                23 марта 2017, 09:57
                Бобровский Дмитрий, 
                1. Все зависит от конкретной задачи. Если все время писать в коллекцию и редко читать, то List<T>, если все же читать, то конечно Dictionary<T>
                2. У List<T> нет реализации ConcurrentList<T>, во всяком  случае в 4.0,  а ConcurrentDictionary<T> — существует.
                3. С Arrays при неаккуратном обращении  возникает boxing/unboxing
                • Бобровский Дмитрий
                  23 марта 2017, 10:49
                  _sg_, а ConcurrentBag как аналог потокобезопасного List? Насчёт выбора типа коллекции не соглашусь. Основное преимущество Dictionary — O(1) в операциях поиска. Рандомизированный доступ к конкретному элементу для List O(1) тоже, если мне память не изменяет. А boxing/unboxing явление довольно частое и скорее относится к категории «прямых рук», нежели к выбору коллекции, имхо.
                • Бобровский Дмитрий
                  23 марта 2017, 12:01

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

                    • Андрей К
                      23 марта 2017, 22:14
                      Dzam, у хэштэйбла есть все шансы обойти. Буквально сегодня тоже тестировал его разновидность.
          • Зарабатывающий Юзер
            23 марта 2017, 01:37
            Бобровский Дмитрий, изначально Вы можете только предположить «сложность» и «емкость» вычислений исключительно эмпирикой.
            Зная заранее данные железа+софта+мануал, Вы изначально построите код так, чтобы избежать ограничений из Вашей эмпирики.

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

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

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

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

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

                Потому и первый же мой коммент в теме сознательный.
      • Сергей Гаврилов
        23 марта 2017, 01:47
        Бобровский Дмитрий, если нужна супер-скорость, то это не актуально.., так как в этом случае о словарях вообще забыть надо… Только массивы…
        • Бобровский Дмитрий
          23 марта 2017, 01:48
          Сергей Гаврилов, да.
        • Зарабатывающий Юзер
          23 марта 2017, 01:49
          Сергей Гаврилов, хоть как-то, не нытьем — так каканьем, суть поста проявляется.
          Хотя проще было сразу оговориться, что про «бэктесты» и моделирование идёт речь в посте!!!!!!!!!!1
  • Зарабатывающий Юзер
    23 марта 2017, 00:50
     P.S. Также можно ускорить процесс, если во время инициализации определить размер массивов.


    В иных случаях не является ли это ОБЯЗАТЕЛЬНЫМ УСЛОВИЕМ при написании программы?
      • Зарабатывающий Юзер
        24 марта 2017, 02:59
        Dzam, я цитировал в общем… для любого языка/среды.
        Ранее было от меня про то, что нужно знать ограничения. Если их нет (только ФП) — тонет вопросов.
  • Андрей К
    23 марта 2017, 01:00
    Есть еще момент. Желательно учитывать какие операции будут использоваться чаще. Чтение/доступ либо модификация списка. Внутри этих контейнеров могут быть реализованы разные структуры и алгоритмы. И какие то заточены для одного, а какие то для другого
  • Андрей К
    23 марта 2017, 01:03
    Прошу прощение, вы про это и написали
  • Сергей Гаврилов
    23 марта 2017, 01:40
    В свое время я тоже исследовал скорость разных списков, словарей… А ребята из крупной прогерской конторе, которые занимались промышленным программированием, сказали, оно конечно все правильно, но не занимайся херней.., профайлер покажет «бутылочные горлышко» и оно будет совсем в другом месте..
     С тех пор так и живем, если у Вас в коллекции 100-1000 да даже 10000 элементов, то и заморачиваться не стоит, ничего Вы не выиграете,  используйте то, что удобно…   
      • Сергей Гаврилов
        23 марта 2017, 13:18
        Dzam, а что тут искать, в этом случае только массив… и ни каких классов внутри массива, только структуры..., только for.., оптимизация внутри for..., ну и тонкий тюнинг, например, веcь double меняем на integer, дату тоже на integer можно поменять..
        Кстати по моим тестам замена класса на структуру в таких задачах скорость больше чем в два раза увеличивала…
        Да еще ищем места, где принудительно мусор собираем..., например если 20 млн… с диска грузанули, то временный список чистим, а потом гарбаж…
  • Roman Ivanov
    23 марта 2017, 01:43
    Не понял за счет чего Dictionary «довольно быстро обрабатывает их (поиск, сортировка).». Поиск ясно, это же хеш-таблица, но как она поможет в сортировке???
  • Зарабатывающий Юзер
    23 марта 2017, 01:48
    Перед тем, как использовать в своем роботе переменные типа Dictionary или List,

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

    Надо так и писать сразу про «бэктест». ПРЯМО в заголовке или первых строках поста!
    • broker25
      23 марта 2017, 09:21
      Счастливый Лузер, тема интересная, а вы флудите
      • Зарабатывающий Юзер
        24 марта 2017, 03:01
        broker25, я не флудил!
        Я разгонял тему, чтобы она  НЕ СГИНУЛА В ПУЧИНЕ г*внопостов!
        Тема реальная!
        Тема жизненная.
        Тема узкоспециализированная.

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

        Рад, что за сутки тема не сгинула, а даже новый вздох приобрела!
  • nbvehrfr
    23 марта 2017, 02:22
    Пост просто жесть.
    какое отношение реализация базовых структур данных иммет к скорости работы торгового терминала?
    Тимофей ты начал копирайтеров нанимать чтобы контент генерировали из ничего?
    тайтл поста не имеет ничего общего со статьей, чистый click bait
    • Дар Ветер
      23 марта 2017, 08:15
      nbvehrfr, понятно что это скорее про работу дотнета но применительно к нинже вполне.
      • nbvehrfr
        23 марта 2017, 19:38
        Dzam, капец для того чтобы терминал не висел не нужно «держать» обработчик OnBarUpdate а реализовать в виде non blocking кода.
          • nbvehrfr
            23 марта 2017, 21:12
            Dzam, один поток висит на OnBarUpdate и проксирует данные в другой поток (Worker) — через очередь или еще что есть в c#, при этом не блокируя caller, те терминал не висит. далее в Worker делаете все что душе угодно с полученными от первого потоко данными.
              • nbvehrfr
                23 марта 2017, 21:41
                Dzam, на скале да. пример кода? 150$/h при сроке 6 месяцев, меньше — выше ставка
                  • Зарабатывающий Юзер
                    24 марта 2017, 03:04
                    Dzam, да тут выходит просто фигня… для чего именно? не понятно.
                    Автор в посте не конкретизирует задачу, приводя просто тесты его. и те с оговоркой «а больше не расскажу/».
              • Андрей К
                23 марта 2017, 22:11
                Dzam, вам предложили все рассчеты убрать в асинхронный Worker. Если простым языком. Например в такой https://professorweb.ru/my/WPF/documents_WPF/level31/31_3.php
                OnBarUpdate при этом просто будет вызвать рассчет воркера и дожидаться результатов не будет, тем самым не блокируя работу. Что и есть асинхронный режим
    • Зарабатывающий Юзер
      24 марта 2017, 03:02
      nbvehrfr, в посте не упомянуто про терминал. как и не упомянуто про бэктест.
      ИЗНАЧАЛЬНО я и просил автора конкретизировать ЦЕЛЬ поста.
  • broker25
    23 марта 2017, 09:20
    Мы же о трейдинге говорим, тогда и смотреть нужно самые частые операции. Добавление элементов? Оч.сомневаюсь. У меня основная задача —  найти элемент списка по ключу или номеру (приходят цены и стаканы). Я и тестил этот вопрос. Dictionary логично заметно быстрее ищет по ключу, чем List. Но простой Dictionary неупорядочен. это мешает, а Ordered очень медленно (по отзывам, сам не проверял) работает. И С. Гаврилов здесь говорил о том, что foreach медленно работает. А как иначе перебирать Dictionary? По списку ключей не уверен, что быстрее. Так что, выбор скорее между List и простым массивом. Не знаю, есть ли там разница в скорости поиска ключа
    • Андрей К
      23 марта 2017, 09:39
      broker25, если так озадачиваться, то нужно проводить масштабные тесты самому. В разных условиях, строить графики распределения результатов. На слово особо верить не стоит, каждое слово нужно перепроверить.
    • Андрей К
      23 марта 2017, 09:44
      broker25, я от шарпа стал далек, но насколько помню, List построен внутри как бинарное дерево. А значит заточен только под определенные задачи.
      Dictionary — это хэштаблица. И подходит для работы с типами данных малой величины и на не больших объемах, потому что показатель производительности всегда константен.

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

    В общем то хотелось бы действительно узнать в каких ситуациях стоит пренебречь удобством, ради выигрыша времени?
      • CloseToAlgoTrading
        23 марта 2017, 12:32
        Dzam, это опять же таки пример инициализации перед выполнением, но почему это нельзя сделать в конвеерном режиме, т.е. данные которые мы уже посчитали остаются неизменными, нам ведь не надо прогонять их снова, просто добавляем новые.
        Я скорее абстракно размышляю, не про нинзю в данном случае, я ее не пользую. У меня интерес скорее личный, для своей системы :)
          • CloseToAlgoTrading
            23 марта 2017, 13:04
            Dzam, ну это уже специфика реалтайм расчета, тут вроде как не нужен весь набор данных же.
              • CloseToAlgoTrading
                23 марта 2017, 13:32
                Dzam, я имею ввиду не нужно всю историю хранить, только 14 тиков.

                я так понимаю вы складываете обьемы там? как вариант хранить в памяти сумму последних 14 тиков, и значение первого тика, потом при каждом апдейте берете сумму — первый тик + последний и у вас новое значение. но это так на вскидку. :)
              • Андрей К
                23 марта 2017, 22:17
                Dzam, в таком примере нужно пробовать LINQ, может суметь обогнать при правильном построении данных.
  • Сергей Гаврилов
    23 марта 2017, 13:34
     Еще раз повторю свою мысль… Вставьте свою прогу в профайлер и Вы узнаете о ней много интересного… И все эти заморочки с типами коллекций покажутся настолько несущественными…
      • Сергей Гаврилов
        23 марта 2017, 14:31
        Dzam, 

        • CloseToAlgoTrading
          23 марта 2017, 15:07
          Сергей Гаврилов, так то профайлером мы конечно пользуемся, коли надо что отловить.
          Но там же люди вроде нинзю трейдер юзают.
          • Сергей Гаврилов
            23 марта 2017, 15:09
            Denis, а что си-шный файл из нинзи в профайлер не влезает? К процессу подключиться нельзя?
      • Андрей К
        23 марта 2017, 22:18
        Dzam, у нинзи нет профилировщика вроде как =)
  • CloseToAlgoTrading
    23 марта 2017, 15:22
    Я хз, я нинзяй не пользуюсь :), но и профайлер не панацея, лучше же сразу красиво все делать, чем потом всю архитектуру переделывать
    • Сергей Гаврилов
      23 марта 2017, 17:05

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

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

  • Зарабатывающий Юзер
    24 марта 2017, 02:54
    Я рад тому, что поспособствовал теме. Не сгинул топик, написанный в неудобное время, в пучину остальных постов.

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

    Наполнить комментариями.
  • Зарабатывающий Юзер
    24 марта 2017, 03:05
    А можно просьбу?
    Я всё же не дал вчера сгинуть посту в пучину.
    Простейший алгоритм запрогить в ниньзе.
    Сравнить просто с иными средами/терминалами.

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

    (в личку всё)

Активные форумы
Что сейчас обсуждают

Старый дизайн
Старый
дизайн