Утечки памяти при тестировании на исторических данных
Atom Ответить
02.04.2012


Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.
Сначал грешил на свою стратегию, потом попробовал запускать пример SampleHistoryTesting многократно,
При достижении 30-40 повторений программа подвисала, не выдавая никаких сообщений.

Далее, для чистоты эксперимента подправил код SampleHistoryTesting:

Код
		private void StartBtn_Click( object sender, RoutedEventArgs e ) {
				
			for( int i = 1; i < 1000; i++ ) {
				Run();

				if( _trader.State != EmulationStates.Started ) { Thread.Sleep( 10 );  }
				if( _trader.State != EmulationStates.Stopped ) { Thread.Sleep( 10 ); }
			}

			MessageBox.Show( "Закончено" );
		}
		
		
		private void Run()
		{
		
		// Ну а тут сам код стратегии, что был раньше в StartBtn_Click 
		
		...
		
		// изменил только период, для более быстрого тестирования
			var startTime = new DateTime(2009, 6, 1);
			var stopTime = new DateTime(2009, 6, 3);
		
		....
		
		}



После запуска данный код через некоторое время выдает Out of memory эксепшн.
(Пробовал, как с включенным выводом инфы в форму, так и выключеным)

на 100 итерациях уже все нормально проходит.

Памяти достаточно - 4 гб озу


Текст исключения:

System.OutOfMemoryException: Выдано исключение типа "System.OutOfMemoryException".

в System.Linq.Buffer`1.ToArray()
в System.Linq.Enumerable.ToArray[TSource](IEnumerable`1 source)
в Ecng.Common.RandomArray`1..ctor(T min, T max, Int32 count)
в StockSharp.Algo.Testing.MarketDataGenerator`1.Init()
в StockSharp.Algo.Testing.TrendMarketDepthGenerator.Init()
в StockSharp.Algo.Testing.EmulationTrader.Start(DateTime startDate, DateTime stopDate)
в SampleHistoryTesting.MainWindow.Run() в C:\Projects\VS\OrigSampleHistoryTesting\MainWindow.xaml.cs:строка 219
в SampleHistoryTesting.MainWindow.StartBtn_Click(Object sender, RoutedEventArgs e) в C:\Projects\VS\OrigSampleHistoryTesting\MainWindow.xaml.cs:строка 63
в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
в System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
в System.Windows.UIElement.RaiseEvent(RoutedEventArgs e)
в System.Windows.Controls.Primitives.ButtonBase.OnClick()
в System.Windows.Controls.Button.OnClick()
в System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(MouseButtonEventArgs e)
в System.Windows.UIElement.OnMouseLeftButtonUpThunk(Object sender, MouseButtonEventArgs e)
в System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
в System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
в System.Windows.UIElement.ReRaiseEventAs(DependencyObject sender, RoutedEventArgs args, RoutedEvent newEvent)
в System.Windows.UIElement.OnMouseUpThunk(Object sender, MouseButtonEventArgs e)
в System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(Delegate genericHandler, Object genericTarget)
в System.Windows.RoutedEventArgs.InvokeHandler(Delegate handler, Object target)
в System.Windows.RoutedEventHandlerInfo.InvokeHandler(Object target, RoutedEventArgs routedEventArgs)
в System.Windows.EventRoute.InvokeHandlersImpl(Object source, RoutedEventArgs args, Boolean reRaised)
в System.Windows.UIElement.RaiseEventImpl(DependencyObject sender, RoutedEventArgs args)
в System.Windows.UIElement.RaiseTrustedEvent(RoutedEventArgs args)
в System.Windows.UIElement.RaiseEvent(RoutedEventArgs args, Boolean trusted)
в System.Windows.Input.InputManager.ProcessStagingArea()
в System.Windows.Input.InputManager.ProcessInput(InputEventArgs input)
в System.Windows.Input.InputProviderSite.ReportInput(InputReport inputReport)
в System.Windows.Interop.HwndMouseInputProvider.ReportInput(IntPtr hwnd, InputMode mode, Int32 timestamp, RawMouseActions actions, Int32 x, Int32 y, Int32 wheel)
в System.Windows.Interop.HwndMouseInputProvider.FilterMessage(IntPtr hwnd, WindowMessage msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
в System.Windows.Interop.HwndSource.InputFilterMessage(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
в MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
в MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
в System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
в MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)




Спасибо:




25 Ответов
Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 02.04.2012
Ответить


zorran Перейти
Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.


Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader
Спасибо:

zorran

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


Проверял на 4.0.19 и 4.0.23
Автор топика
Спасибо:

art.tsgnet

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


А где взять версию 4.1 ?
в box.com пока вижу последнюю версию 4.0.23
Спасибо:

Alexander

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


art.tsgnet Перейти
А где взять версию 4.1 ?
в box.com пока вижу последнюю версию 4.0.23


Codeplex, папка dev
Спасибо:

gaifredo

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


Скачал новую версию, и запустил в ней SampleHistoryTesting.
В ответ тишина, т.е. кривая эквилити не обновляется. Сообщение о том, что расчет закончен не отображается.
На предыдущих версиях данный пример работал
Спасибо:

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 17.04.2012
Ответить


gaifredo Перейти
Скачал новую версию, и запустил в ней SampleHistoryTesting.
В ответ тишина, т.е. кривая эквилити не обновляется. Сообщение о том, что расчет закончен не отображается.
На предыдущих версиях данный пример работал


4.1? Архив с данными тоже был скачал новый?
Спасибо:

gaifredo

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


архив данных от 4.0
при попытке запустить гидру, получаю ошибку, о которой отписался в соответствующем разделе
Спасибо:

Alexander

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


gaifredo Перейти
архив данных от 4.0


данные менялись, используйте новые
Спасибо:

art.tsgnet

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


Mikhail Sukhov Перейти
zorran Перейти
Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.


Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader

в 4.1 не нашел такого
Спасибо:

Alexander

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


art.tsgnet Перейти
Mikhail Sukhov Перейти
zorran Перейти
Обнаружил утечки памяти при тестировании на исторических данных, при многократном повторении.


Спасибо посмотрим. Это какая версия S#? В 4.1 у нас новый тестер. Лучше его использовать. Называется LowMemEmulationTrader

в 4.1 не нашел такого


Уже переименован в EmulationTrader
Спасибо:

paveld

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


У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?
Спасибо:

Alexander

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


paveld Перейти
У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?


Конечно нужно. Какой интервал тестирования, сколько памяти потребляло в 4.0, сколько потребляет в 4.1.
Какая конкретно версия с codeplex.
Спасибо:

paveld

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


Alexander Mukhanchikov Перейти
paveld Перейти
У меня на 4.1 в примере SampleHistoryTesting утечка памяти тоже возникает. Может нужно какие-нть данные представить по использованию памяти?


Конечно нужно. Какой интервал тестирования, сколько памяти потребляло в 4.0, сколько потребляет в 4.1.
Какая конкретно версия с codeplex.


Интервал тестирования не менял (стоит с 01.06.2009 по 01.09.2009)
На 4.0.0.23 в пике 2174012 Кб
На 4.1 (с codeplex stocksharp-17261.zip) в пике 2117100 Кб

Спасибо:

alexeev.evg

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


Аналогичная ситуация. Если запускать трейдер много раз, возникало исключение OutOfMemory. С каждой новой итерацией оказывалось съеденным все больше памяти.
Код

static void Main(string[] args)
{
	for (int i = 1; i <= 100; i ++)
	{
		using (var temp = new MyClass())
		{
			temp.StartTest(date1, date2);
		}
		GC.Collect();
		GC.WaitForFullGCComplete();
		//с каждой новой итерацией занятая память в этой точке растет
	}
	Console.ReadLine();
}

public class MyClass : IDisposable
{
	private Security _security1;
	private Security _security2;
	private Portfolio _portfolio;
	private MarketDepth _depth1;
	private MarketDepth _depth2;
	private BaseTrader _trader;

	public MyClass()
	{
	}

	public void StartTest(DateTime date1, DateTime date2)
	{
		//запуск трейдера
	}

	public void Dispose()
	{
		if (_trader != null)
		_	trader.Dispose();
		_security1 = null;
		_security2 = null;
		_portfolio = null;
		_depth1 = null;
		_depth2 = null;
		_trader = null;
	}
}


После танцев с бубном удалось выяснить, что проблема в буфере экземпляра EmulationTrader - приватное поле типа Ecng.Collections.BlockingQueue<>. Стало ясно, что объекты данной коллекции остаются в памяти, сборщик мусора почему-то их не трогает. При запуске каждого нового экземпляра EmulationTrader объем занимаемой памяти растет. Уменьшение свойства BufferSize приводит к уменьшению размера прироста памяти, но проблема остается.
Решил проблему очисткой буфера в методе Dispose() MyClass (прирост занятой памяти или вообще прекратился или сильно замедлился):

var value= _trader.GetType().GetField("#=qtcoS5HXJh4KLzrXhXg6zgg==", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(_trader);
value.GetType().GetMethod("Clear").Invoke(value, null);
_trader.Dispose();

Думаю, если очищать буфер в методе Dispose класса EmulationTrader, хуже не станет.
Спасибо:

pyhta4og

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


alexeev.evg,

из вашего кода несовсем ясно, сколько экземпляров EmulationTrader используется?

конструкция
<code>
using (var temp = new MyClass())
{
temp.StartTest(date1, date2);
}
</code>

как мне кажется стартует EmulationTrader и сразу его останавливает в Dispose не дожидаясь пока он дотестирует.

Вы можете привести полный код примера чтобы я мог воспроизвести утечку?
Спасибо:

ak

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


А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). Если запускать тестирование с включенной генерацией стакана - обычно ±150 созданных и затем даже принудительно удаленных EmulationTrader'ов хватает чтобы заполнить ±1200 MB оперативы (после очень скоро следует OutOfMemoryEx).

Виновником, как я думаю, является TrendMarketDepthGenerator, содержащий объект(ы) типа Ecng.Common.RandomArray, которые по какой-то причине не очищаются GC, но при этом содержат и накапливают огромное количество [int]. На картинке - состояние приложения после прогона на 48 EmulationTrader'ах и удалении каждого. 89% занимают неудаленные массивы:

Click for large view - Uploaded with Skitch

Если сделать нечто, предложенное alexeev.evg выше, при удалении EmulationTrader'а (взял первый попавшийся объект содержащий Ecng.Common.RandomArray), для последней версии S# это:

Код

// try to cleanup memory, private field in EmulationTrader
//{Ecng.Collections.CachedSynchronizedDictionary<StockSharp.BusinessEntities.Security,StockSharp.Algo.Testing.MarketDepthGenerator>} 
var value = context.Value.Trader.GetType().GetField("#=qHvivsYU2tNspR3_h$VF0nqA$yDC50HFX_RHAxeUi6UE=", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(context.Value.Trader);
value.GetType().GetMethod("Clear").Invoke(value, null);

context.Value.Trader.Dispose();
context.Value.Trader = null;


То занимая память приложения уменьшится примерно вдвое.

Коллеги, можете посмотреть, что с этим можно сделать?
Спасибо: Геннадий Ванин (Gennady Vanin)

FiNick

Фотография
Благотворитель
Дата: 31.01.2013
Ответить


Та же фигня. Пол дня искал в своем коде утечки, пока на форум не зашел=/
Спасибо: Геннадий Ванин (Gennady Vanin)

VassilSanych

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


Хорошая статейка
http://msdn.microsoft.co...s/magazine/jj863136.aspx
В самом низу есть список новых потоконезависимых коллекций .net 4.0
Как вариант замены деревянных велосипедов.
Спасибо: Den Геннадий Ванин (Gennady Vanin)

Den

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


VassilSanych Перейти
Хорошая статейка
https://msdn.microsoft.co...s/magazine/jj863136.aspx
В самом низу есть список новых потоконезависимых коллекций .net 4.0
Как вариант замены деревянных велосипедов.


А кто-нить в S# тестил разницу между SynchronizedDictionary и ConcurrentDictionary?
Спасибо:

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 01.02.2013
Ответить


FiNick Перейти
Та же фигня. Пол дня искал в своем коде утечки, пока на форум не зашел=/


На последней версии воспроизводиться?
Спасибо:

Den

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


Mikhail Sukhov Перейти
На последней версии воспроизводиться?


ak выше писал "А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). "
Спасибо:

Mikhail Sukhov

Фотография
Автор статей Программист Трейдер
Дата: 01.02.2013
Ответить


Den Перейти
Mikhail Sukhov Перейти
На последней версии воспроизводиться?


ak выше писал "А между тем, проблема с EmulationTrader осталась и в последней версии S# (4.1.7.0). "


Я про кодеплекс.
Спасибо:

ak

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


Потестил на последней сборке #22766 - но моих тестах ситуация изменилась кардинально.

Тестировал на 48 EmulationTrader'ах и 3 торговых днях.

Предыдущая версия (не уверен, что 4.1.7.0, возможно я обновлялся из trunk'а):
Пиковое потребление: 1200+ МБ
Потребление после завершения теста и удаления EmulationTrader'ов: ~600 МБ



Новая версия: #22766
Пиковое потребление: ~500 МБ
Потребление после завершения теста и удаления EmulationTrader'ов: ~140 МБ



Спасибо. Позже я еще внимательно посмотрю на CandleManagerContainer, может и он подтекает )
Спасибо:

FiNick

Фотография
Благотворитель
Дата: 07.02.2013
Ответить


Мне не помогло. Взял с кодеплекса последний changeset 22848, референсы с транка, версия 4.1.8.
Один прогон теста на данных ордерлога за месяц занимают 2Гб, затем делаю Dispose всему чему могу, экземпляру трейдера в том числе, делаю GC.Collect() на всякий случай, память занимаемая приложением не уменьшается. Затем запускается новый прогон теста на том же месяце, память уходит за 7Гб, комп помирает
Спасибо:

ak

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


Нужно убедиться, что все объекты входящие в конструирование трейдера нигде не остаются висеть, внимательно просмотрите у себя:

  1. security
  2. storage
  3. portfolio
  4. marketDepthGenerator
  5. candleManager
  6. candleSeries
  7. strategy

Я еще делаю это, записав результат прогона:

Код
trader.StateChanged += (oldState, newState) =>
                {
                    if (trader.State == EmulationStates.Stopped)
                    {
                        trader.UnRegisterMarketDepth(marketDepthGenerator);
                        marketDepthGenerator = null;
                    }
                };

....

trader.StateChanged += (oldState, newState) =>
            {
                if (trader.State == EmulationStates.Stopped)
                {
                    strategy.Stop();
                    candleManager = null;
                    storage = null;
                }        
            };

....

strategy.Trader.Dispose();
strategy.Trader = null;
strategy.Dispose();


Особенно обратите внимание на marketDepthGenerator = null; - из объекта трейдера его не достать никак, поэтому я прицепил этот обработчик в scope'е где у меня есть доступ к только что созданному локальному объекту marketDepthGenerator.

Воспользуйтесь CLR Profiler (http://www.microsoft.com/en-us/download/details.aspx?id=16273), он позволяет очень наглядно посмотреть Managed Heap для запущенного приложения - сразу станет понятно, где течет.
Спасибо:


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

loading
clippy