Stock# с несколькими квиками
Atom
03.08.2010
Alexander


Со сколькими копиями квика можно безболезненно запускать одного робота? Как происходит экспорт через DDE в Stock# - одинаковые данные, я так понимаю, фильтруются?

Вопрос возник не случайно - сейчас с 7ми квиками роботы съедают до 50-60% от нашего довольно мощного сервера (на каждом квике запущен 1-2 робота, каждый робот запускается 1 секунду). Стоит ли искать ошибку, пытаться оптимизировать самого робота или лучше закинуть часть квиков на другой сервер?


Теги:


Спасибо:


< 1 2 3 4  > >>
Mikhail Sukhov

Фотография
Дата: 11.08.2010
Ответить


Можете сделать проще. Отнаследоваться от QuikTrader, переопределить ReStartExport и там прописать свою логику проверки - нужно перезапускать экспорт или не нужно.

Спасибо:

Иванов Андрей

Фотография
Дата: 12.08.2010
Ответить


Комментарий о чём? ;) У вас синтетический случай, поэтому такой огромный прирост.

Если вопрос "почему?", то потому что когда пишете сразу в файл, копирование в памяти всего одно, а когда у вас несколько раз осуществляется операция конкатенации, вы копируете данные много раз. Из неочевидного строки создаются на хипе, а не на стеке, их создаётся много и GC освобождает занимаемую ими память. В случае 2 выделения/ освобождения памяти практически нет, все данные в кэше процессора, поэтому он работает на максимальной скорости, а не спотыкается об ожидание данных из медленной памяти.

Ну а запись в файл, как вы уже заметили, происходит очень быстро =) Потому что вывод буферизованный -- когда вы делаете 5 вызовов вместо одного, они все пишут в один буфер и оверхеада по сравнению с записью всей строки одним вызовом почти нет.

Если будете увлекаться файлами, столкнётесь с ещё одной неочевидной штукой. Называется IOPS. Пока читаете/пишете линейно, скорости обычного SATA будет достаточно -- придумана куча технологий для ускорения линейного доступа (кэши, буферы, группировка вызовов операционной системой, NCQ), но как только чтение станет случайным, пиковые 400-500 IOPS SAS-диска за тысячу долларов покажутся жалкой пародией на скорость =) Потому что в переводе на мегабайты получится чуть больше 10 мегабайтов в секунду, это в 10-15 раз меньше современного бытового SATA-диска на линейном доступе.

Спасибо:

Mikhail Sukhov

Фотография
Дата: 12.08.2010
Ответить


В том то и дело, что я устанавливаю AutoFlush = true. А это отключает всякую буферизацию. А если бы был буфер, я правильно понимаю, что сброс буфера в файл происходит асинхронно?

Насчет хипа - я так и думал. string - это ведь класс. Но ведь буферы у StreamWriter наверняка хранят не массив object, а массивы byte[]. Последние - это перевод переданных строк в файловой представление. Так вот, а массивы так же хранятся в хипе, и так же нужно время на перевод из строки в byte[]. Где же тогда экономия?

Про рандом и последовательный доступ понятно. Я читал об этом раньше. Надеюсь в S# не придется с этим столкнутся. Не хотелось бы погрязнуть в системных тонкостях =)

Спасибо:

Иванов Андрей

Фотография
Дата: 13.08.2010
Ответить


AutoFlush занимается тем, что сбрасывает буферы самого StreamWriter в подлежащий стрим. Использование этого флага с файлами ничего не меняет. Писать можно без буферов, потому что под StreamWriter должен быть какой-то FileStream, который сам будет буферизовать вывод, плюс есть буфер ядра.

Выключить буферизацию в ядре можно только ключом в реестре на всю систему или открывая файл. Отключить буфер после открытия файла, по- моему, невозможно -- необходимость буферизации известна до использования файла. По умолчанию буфер ядра в Windows Server несколько мегабайтов, сколько он в десктопах, не знаю. В десктопах и серверах разные схемы кэширования и буферов по умолчанию, в серверах сейчас (2008/2008R2) кэширование по умолчанию может съесть всю память и программам придётся свапиться =)

Сброс буфера ядра асинхронный. Сбросы прикладных буферов синхронные.

Массивы буферов не создаются и не удаляются 100 тысяч раз в секунду =) Они один раз создаются и живут какое-то продолжительное время. Когда вы делаете s1 += s2;, вы создаёте один объект (результат конкатенации s1 и s2) и отдаёте GC один объект (тот, на который до операции указывал s1). В случае с буферами все остаются при своих, происходит только одно копирование. Самый шик, когда строка не помещается в Gen 0 и отправляется в LOH -- в этом случае основную часть процессорного времени занимает удаление объектов =) Но это не наш случай, потому что строка должна быть больше 40 тысяч обычных символов (а необычных, типа индийских, ещё на треть меньше).

Спасибо:

Alexander

Фотография
Дата: 15.08.2010
Ответить


т.е. перезапускать экспорт только в том случае, если нет подключения:

public class OwnQuikTrader : QuikTrader { public OwnQuikTrader(string path, string ddeServer, string dllName) : base(path, ddeServer, dllName)

   public override void ReStartExport()
    {
        if (!IsConnected)
            base.ReStartExport();
    }
}

?

Спасибо:

Mikhail Sukhov

Фотография
Дата: 16.08.2010
Ответить


Да, так, если такого поведения достаточно.

Спасибо:

Mikhail Sukhov

Фотография
Дата: 16.08.2010
Ответить


Массив буферов создается один раз, согласен (возможно еще как-то увеличивается уменьшается, но это не так важно). Но сами то буферы нет. Они создаются так же часто, как мы вызываем Write. string в byte[] - создаем, int в byte[] - создаем, DateTime в byte[] - создаем... Тоесть, в любом случаем необходимо произвести конфертацию, а это создание массива байтов.

Копирование byte[] в массив буферов?

Спасибо:

Иванов Андрей

Фотография
Дата: 17.08.2010
Ответить


Не, массивы байтов не создаются, создаются строки. Много-много строк (иногда char[]).

Для начала форматная строка парсится (это TextWriter.Write).

public virtual void Write(string format, object arg0, object arg1) { this.Write(string.Format(this.FormatProvider, format, new object[] { arg0, arg1 }));

Из-за того, что выделенной памяти не хватило на создание конечной строки (размер форматной + 16 символов), происходит реаллок с выделением памяти в два раза больше и копированием. При парсинге форматной строки аллоки поменьше для внутреннего использования. Копирований, соответственно, пачка. Тут основная часть CPU и съедается.

Я писал сделки в файлы инструментов и каждый раз (запись сделки) считал путь к имени файла инструмента. Кэширование этой операции дало примерно пятикратный прирост скорости записи. Закэшировал остальные операции вычисления пути (тоже просто конкатенации и Path.Combine), прирост ещё раза в два.

Форматная строка вида

string dealStr = string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}; {1:T};{2};{3};{4};{5}", _id, _timeStamp, _price, _count, _volume, _operation.HasValue ? _operation.ToString() : "0");

по скорости почти идентична просто конкатенации или StringBuilder. Предположу, что здесь хватило выделенного начального буфера (40 символов) и форматтер InvariantCulture работает быстрее CurrentCulture. Выяснять лениво, мне достаточно того, что так писать красивее, чем через + и работает некритично медленнее.

Писать напрямую в файл не стал, потому что после включения кэширования путей время полного копирования сделок за сутки составляет секунд 10. А если вместо форматной строки выше писать константу такой же длины, скорость пару секунд. 2 секунды или 10, роли не играет.

Спасибо:

Alexander

Фотография
Дата: 23.08.2010
Ответить


Опять проблема возникла с запуском нескольких стратегий на нескольких квиках. Сейчас на сервере запущено 7 квиков и суммарно должно было бы запуститься 13 стратегий.

Я запускаю следующим образом:

//цикл по всем стратегиям //для каждой из стратегий получаю список из счетов для которых данная стратегия не была запущена TimeFrameStrategy tfStrategy = null; //в зависимости от стратегии tfStrategy инициализирую конструктором нужного класса (наследуемого как раз от TimeFrameStrategy) //регистрирую ТФ

tfStrategy.Log += Strategy_OnLog; var newDateTimeDir = string.Format("Logs\{0}{1:00}{2:00}", DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day); if (!Directory.Exists(newDateTimeDir)) { Directory.CreateDirectory(newDateTimeDir);

var logger = new StrategyLogger("{0}\_{1} _{2}.txt".Put(newDateTimeDir, Enum.GetValues(typeof (StrategiesName)).GetValue(i), account.Account)); logger.Strategies.Add(tfStrategy);

_strategyManager.Register(tfStrategy, port, riFut); // где port - портфель нужный, riFut - фьюч из Securities. и то и то не null tfStrategy.Start();

В результате у меня для всех 13 стратегий создаются логи, но работают не все (в разные запуски разное число, от 4 до 8 было) - в логе неработающих стратегий лишь строчка 11:06:42.8281250 Volume_1_01:00:05 Volume_1 запущена. и ничего более, хотя при каждом вызове OnProcess в лог должно что-то писаться.

Вот сейчас в 8 стратегиях из 13 пишется, в 5 - лишь 1 эта строчка.

С чем это связано?

Спасибо:

Alexander

Фотография
Дата: 23.08.2010
Ответить


Попробовал запускать стратегии после того, как добавил их в StrategyManager - не помогло, последние 5 из добавленных стратегий всё равно не работают - в лог пишут лишь 1 строку.

Спасибо:
< 1 2 3 4  > >>

Добавить файлы через драг-н-дроп, , или вставить из буфера обмена.

loading
clippy