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 строку.
|
|
Спасибо:
|
|
|
|