I am currently investigating a performance issue in an application and have highlighted the following;
I have a class -
public static class CommonIcons
{
...
public static readonly System.Windows.Media.ImageSource Attributes = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png");
...
}
As a test harness I then have the following code using this class to show the issue -
for (int loop = 0; loop < 20000; loop++)
{
// store time before call
System.Windows.Controls.Image image = new System.Windows.Controls.Image
{
Source = CommonIcons.Attributes,
Width = 16,
Height = 16,
VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true
};
// store time after call
// log time between before and after
}
At the start of the loop the time difference is less than 0.001 seconds, but after 20000 goes this has increased to 0.015 seconds.
If I don't use the static member and directly refer to my icon, then I do not have the performance hit, i.e.
for (int loop = 0; loop < 20000; loop++)
{
// store time before call
System.Windows.Controls.Image image = new System.Windows.Controls.Image
{
Source = typeof(CommonIcons).Assembly.GetImageFromResourcePath("Resources/attributes.png"),
Width = 16,
Height = 16,
VerticalAlignment = VerticalAlignment.Center,
SnapsToDevicePixels = true
};
// store time after call
// log time between before and after
}
But in my real world program I don't want to be creating the imagesource on every call (increased memory until a garbage collection), hence why a static member is used. However I also cannot live with the performance hit.
Can someone explain why the original code is creating this performance hit? And also a better solution for what I am trying to do?
Thanks
It smells like something to do with garbage collection. I wonder whether there's some kind of coupling between the ImageSource and the Image which is causing problems in your first case. Have you looked to see what the memory usage of your test harness looks like in each case?
Out of interest, what happens if you set the Source to null at the end of each iteration? I know this is a bit silly, but then that's a natural corollary of it being a test harness :) It might be a further indication that it's a link between the source and the image...
Can you store only constant strings like "Resources/attributes.png" in your CommonIcons class ?
The difference is not between static member or not, but it is that in the first version you create 20000 images all having the same imagesource. I don't know exactly what is going on, but under the hood there may be delegates created automatically which handle communication between imagesource and image and everytime if an event in the imagesource occurs, 20000 clients needs to be notified, so this is a large performance hit.
In the second version, each of the 20000 created images have their own imagesource so you don't experience this overhead.
Note that you should always dispose graphical objects like Images with their Dispose()-method after you are done with them, this will speed up your application a bit and lowers your general memory usage.
Related
I recently had to check in this monstrosity into production code to manipulate private fields in a WPF class: (tl;dr how do I avoid having to do this?)
private static class MemoryPressurePatcher
{
private static Timer gcResetTimer;
private static Stopwatch collectionTimer;
private static Stopwatch allocationTimer;
private static object lockObject;
public static void Patch()
{
Type memoryPressureType = typeof(Duration).Assembly.GetType("MS.Internal.MemoryPressure");
if (memoryPressureType != null)
{
collectionTimer = memoryPressureType.GetField("_collectionTimer", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Stopwatch;
allocationTimer = memoryPressureType.GetField("_allocationTimer", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null) as Stopwatch;
lockObject = memoryPressureType.GetField("lockObj", BindingFlags.Static | BindingFlags.NonPublic)?.GetValue(null);
if (collectionTimer != null && allocationTimer != null && lockObject != null)
{
gcResetTimer = new Timer(ResetTimer);
gcResetTimer.Change(TimeSpan.Zero, TimeSpan.FromMilliseconds(500));
}
}
}
private static void ResetTimer(object o)
{
lock (lockObject)
{
collectionTimer.Reset();
allocationTimer.Reset();
}
}
}
To understand why I would do something so crazy, you need to look at MS.Internal.MemoryPressure.ProcessAdd():
/// <summary>
/// Check the timers and decide if enough time has elapsed to
/// force a collection
/// </summary>
private static void ProcessAdd()
{
bool shouldCollect = false;
if (_totalMemory >= INITIAL_THRESHOLD)
{
// need to synchronize access to the timers, both for the integrity
// of the elapsed time and to ensure they are reset and started
// properly
lock (lockObj)
{
// if it's been long enough since the last allocation
// or too long since the last forced collection, collect
if (_allocationTimer.ElapsedMilliseconds >= INTER_ALLOCATION_THRESHOLD
|| (_collectionTimer.ElapsedMilliseconds > MAX_TIME_BETWEEN_COLLECTIONS))
{
_collectionTimer.Reset();
_collectionTimer.Start();
shouldCollect = true;
}
_allocationTimer.Reset();
_allocationTimer.Start();
}
// now that we're out of the lock do the collection
if (shouldCollect)
{
Collect();
}
}
return;
}
The important bit is near the end, where it calls the method Collect():
private static void Collect()
{
// for now only force Gen 2 GCs to ensure we clean up memory
// These will be forced infrequently and the memory we're tracking
// is very long lived so it's ok
GC.Collect(2);
}
Yes, that's WPF actually forcing a gen 2 garbage collection, which forces a full blocking GC. A naturally occurring GC happens without blocking on the gen 2 heap. What this means in practice is that whenever this method is called, our entire app locks up. The more memory your app is using, and the more fragmented your gen 2 heap is, the longer it will take. Our app presently caches quite a bit of data and can easily take up a gig of memory and the forced GC can lock up our app on a slow device for several seconds -- every 850 MS.
For despite the author's protestations to the contrary, it is easy to arrive at a scenario where this method is called with great frequency. This memory code of WPF's occurs when loading a BitmapSource from a file. We virtualize a listview with thousands of items where each item is represented by a thumbnail stored on disk. As we scroll down, we are dynamically loading in those thumbnails, and that GC is happening at maximum frequency. So scrolling becomes unbelievably slow and choppy with the app locking up constantly.
With that horrific reflection hack I mentioned up top, we force the timers to never be met, and thus WPF never forces the GC. Furthermore, there appear to be no adverse consequences -- memory grows as one scrolls and eventually a GC is triggered naturally without locking up the main thread.
Is there any other option to prevent those calls to GC.Collect(2) that is not so flagrantly hideous as my solution? Would love to get an explanation for what the concrete problems are that might arise from following through with this hack. By that I mean problems with avoiding the call to GC.Collect(2). (seems to me the GC occurring naturally ought to be sufficient)
Notice: Do this only if it causes a bottleneck in your app, and make sure you understand the consequences - See Hans's answer for a good explanation on why they put this in WPF in the first place.
You have some nasty code there trying to fix a nasty hack in the framework... As it's all static and called from multiple places in WPF, you can't really do better than use reflection to break it (other solutions would be much worse).
So don't expect a clean solution there. No such thing exists unless they change the WPF code.
But I think your hack could be simpler and avoid using a timer: just hack the _totalMemory value and you're done. It's a long, which means it can go to negative values. And very big negative values at that.
private static class MemoryPressurePatcher
{
public static void Patch()
{
var memoryPressureType = typeof(Duration).Assembly.GetType("MS.Internal.MemoryPressure");
var totalMemoryField = memoryPressureType?.GetField("_totalMemory", BindingFlags.Static | BindingFlags.NonPublic);
if (totalMemoryField?.FieldType != typeof(long))
return;
var currentValue = (long) totalMemoryField.GetValue(null);
if (currentValue >= 0)
totalMemoryField.SetValue(null, currentValue + long.MinValue);
}
}
Here, now your app would have to allocate about 8 exabytes before calling GC.Collect. Needless to say, if this happens you'll have bigger problems to solve. :)
If you're worried about the possibility of an underflow, just use long.MinValue / 2 as the offset. This still leaves you with 4 exabytes.
Note that AddToTotal actually performs bounds checking of _totalMemory, but it does this with a Debug.Assert here:
Debug.Assert(newValue >= 0);
As you'll be using a release version of the .NET Framework, these asserts will be disabled (with a ConditionalAttribute), so there's no need to worry about that.
You've asked what problems could arise with this approach. Let's take a look.
The most obvious one: MS changes the WPF code you're trying to hack.
Well, in that case, it pretty much depends on the nature of the change.
They change the type name/field name/field type: in that case, the hack will not be performed, and you'll be back to stock behavior. The reflection code is pretty defensive, it won't throw an exception, it just won't do anything.
They change the Debug.Assert call to a runtime check which is enabled in the release version. In that case, your app is doomed. Any attempt to load an image from disk will throw. Oops.
This risk is mitigated by the fact that their own code is pretty much a hack. They don't intend it to throw, it should go unnoticed. They want it to sit quiet and fail silently. Letting images load is a much more important feature which should not be impaired by some memory management code whose only purpose is to keep memory usage to a minimum.
In the case of your original patch in the OP, if they change the constant values, your hack may stop working.
They change the algorithm while keeping the class and field intact. Well... anything could happen, depending on the change.
Now, let's suppose the hack works and disables the GC.Collect call successfully.
The obvious risk in this case is increased memory usage. Since collections will be less frequent, more memory will be allocated at a given time. This should not be a big issue, since collections would still occur naturally when gen 0 fills up.
You'd also have more memory fragmentation, this is a direct consequence of fewer collections. This may or may not be a problem for you - so profile your app.
Fewer collections also means fewer objects are promoted to a higher generation. This is a good thing. Ideally, you should have short-lived objects in gen 0, and long-lived objects in gen 2. Frequent collections will actually cause short-lived objects to be promoted to gen 1 and then to gen 2, and you'll end up with many unreachable objects in gen 2. These will only be cleaned up with a gen 2 collection, will cause heap fragmentation, and will actually increase the GC time, since it'll have to spend more time compacting the heap. This is actually the primary reason why calling GC.Collect yourself is considered a bad practice - you're actively defeating the GC strategy, and this affects the whole application.
In any case, the correct approach would be to load the images, scale them down and display these thumbnails in your UI. All of this processing should be done in a background thread. In the case of JPEG images, load the embedded thumbnails - they may be good enough. And use an object pool so you don't need to instantiate new bitmaps each time, this completely bypasses the MemoryPressure class problem. And yes, that's exactly what the other answers suggest ;)
I think what you have is just fine. Well done, nice hack, Reflection is an awesome tool to fix wonky framework code. I've used it myself many times. Just limit its usage to the view that displays the ListView, it is too dangerous to have it active all the time.
Noodling a bit about the underlying problem, the horrid ProcessAdd() hack is of course very crude. It is a consequence of BitmapSource not implementing IDisposable. A questionable design decision, SO is filled with questions about it. However, about all of them are about the opposite problem, this timer not being quick enough to keep up. It just doesn't work very well.
There isn't anything you can do to change the way this code works. The values it works off are const declarations. Based on values that might have been appropriate 15 years ago, the probable age of this code. It starts at one megabyte and calls "10s of MB" a problem, life was simpler back then :) They forgot to write it so it scales properly, GC.AddMemoryPressure() would probably be fine today. Too little too late, they can't fix this anymore without dramatically altering program behavior.
You can certainly defeat the timer and avoid your hack. Surely the problem you have right now is that its Interval is about the same as the rate at which a user scrolls through the ListView when he doesn't read anything but just tries to find the record of interest. That's a UI design problem that's so common with list views with thousands of rows, an issue you probably don't want to address. What you need to do is cache the thumbnails, gathering the ones you know you'll likely need next. Best way to do that is to do so in a threadpool thread. Measure time while you do this, you can spend up to 850 msec. That code is however not going to be smaller than what you have now, not much prettier either.
.NET 4.6.2 will fix it by killing the MemoryPressure class alltogether. I just have checked the preview and my UI hangs are entirely gone.
.NET 4.6 implemements it
internal SafeMILHandleMemoryPressure(long gcPressure)
{
this._gcPressure = gcPressure;
this._refCount = 0;
GC.AddMemoryPressure(this._gcPressure);
}
whereas pre .NET 4.6.2 you had this crude MemoryPressure class which would force a GC.Collect every 850ms (if in between no WPF Bitmaps were allocated) or every 30s regardless how much WPF bitmaps you did allocate.
For reference the old handle was implemented like
internal SafeMILHandleMemoryPressure(long gcPressure)
{
this._gcPressure = gcPressure;
this._refCount = 0;
if (this._gcPressure > 8192L)
{
MemoryPressure.Add(this._gcPressure); // Kills UI interactivity !!!!!
return;
}
GC.AddMemoryPressure(this._gcPressure);
}
This makes a huge difference as you can see the GC suspension times drop dramatically in a simple test application I did write to repro the issue.
Here you see that the GC suspension times did drop from 2,71s down to 0,86s. This remains nearly constant even for multi GB managed heaps. This also increases overall application performance because now the background GC can do its work where it should: In the background. This prevents sudden halts of all managed threads which can continue to do work happily although the GC is cleaning things up. Not many people are aware what background GC gives them but this makes a real world difference of ca. 10-15% for common application workloads. If you have a multi GB managed application where a full GC can take seconds you will notice a dramatic improvement. In some tests were an application had a memory leak (5GB managed heap, full GC suspend time 7s) I did see 35s UI delays due to these forced GCs!
To the updated question regarding what the concrete problems are you may encounter by using the reflection approach, I think #HansPassant was thorough in his assessment of your specific approach. But more generally speaking, the risk you run with your current approach is the same risk you run with using any reflection against code you don't own; it can change underneath of you in the next update. So long as you're comfortable with that, the code you have should have negligible risk.
To hopefully answer the original question, there may be a way to workaround the GC.Collect(2) problem by minimizing the number of BitmapSource operations. Below is a sample app which illustrates my thought. Similar to what you described, it uses a virtualized ItemsControl to display thumbnails from disk.
While there may be others, the main point of interest is how the thumbnail images are constructed. The app creates a cache of WriteableBitmap objects upfront. As list items are requested by the UI, it reads the image from disk, using a BitmapFrame to retrieve the image information, primarily pixel data. A WriteableBitmap object is pulled from the cache, it's pixel data is overwritten, then it is assigned to the view-model. As existing list items fall out of view and are recycled, the WriteableBitmap object is returned to the cache for later reuse. The only BitmapSource-related activity incurred during that entire process is the actual loading of the image from disk.
It is worth noting that, that the image returned by the GetBitmapImageBytes() method must be exactly the same size as those in the WriteableBitmap cache for this pixel-overwrite approach to work; currently 256 x 256. For simplicity's sake, the bitmap images I used in my testing were already at this size, but it should be trivial to implement scaling, as needed.
MainWindow.xaml:
<Window x:Class="VirtualizedListView.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="500">
<Grid>
<ItemsControl VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
VirtualizingStackPanel.CleanUpVirtualizedItem="VirtualizingStackPanel_CleanUpVirtualizedItem"
ScrollViewer.CanContentScroll="True"
ItemsSource="{Binding Path=Thumbnails}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="White" BorderThickness="1">
<Image Source="{Binding Image, Mode=OneTime}" Height="128" Width="128" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.Template>
<ControlTemplate>
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
Padding="{TemplateBinding Control.Padding}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<ScrollViewer Padding="{TemplateBinding Control.Padding}" Focusable="False">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />
</ScrollViewer>
</Border>
</ControlTemplate>
</ItemsControl.Template>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace VirtualizedListView
{
public partial class MainWindow : Window
{
private const string ThumbnailDirectory = #"D:\temp\thumbnails";
private ConcurrentQueue<WriteableBitmap> _writeableBitmapCache = new ConcurrentQueue<WriteableBitmap>();
public MainWindow()
{
InitializeComponent();
DataContext = this;
// Load thumbnail file names
List<string> fileList = new List<string>(System.IO.Directory.GetFiles(ThumbnailDirectory));
// Load view-model
Thumbnails = new ObservableCollection<Thumbnail>();
foreach (string file in fileList)
Thumbnails.Add(new Thumbnail(GetImageForThumbnail) { FilePath = file });
// Create cache of pre-built WriteableBitmap objects; note that this assumes that all thumbnails
// will be the exact same size. This will need to be tuned for your needs
for (int i = 0; i <= 99; ++i)
_writeableBitmapCache.Enqueue(new WriteableBitmap(256, 256, 96d, 96d, PixelFormats.Bgr32, null));
}
public ObservableCollection<Thumbnail> Thumbnails
{
get { return (ObservableCollection<Thumbnail>)GetValue(ThumbnailsProperty); }
set { SetValue(ThumbnailsProperty, value); }
}
public static readonly DependencyProperty ThumbnailsProperty =
DependencyProperty.Register("Thumbnails", typeof(ObservableCollection<Thumbnail>), typeof(MainWindow));
private BitmapSource GetImageForThumbnail(Thumbnail thumbnail)
{
// Get the thumbnail data via the proxy in the other app domain
ImageLoaderProxyPixelData pixelData = GetBitmapImageBytes(thumbnail.FilePath);
WriteableBitmap writeableBitmap;
// Get a pre-built WriteableBitmap out of the cache then overwrite its pixels with the current thumbnail information.
// This avoids the memory pressure being set in this app domain, keeping that in the app domain of the proxy.
while (!_writeableBitmapCache.TryDequeue(out writeableBitmap)) { Thread.Sleep(1); }
writeableBitmap.WritePixels(pixelData.Rect, pixelData.Pixels, pixelData.Stride, 0);
return writeableBitmap;
}
private ImageLoaderProxyPixelData GetBitmapImageBytes(string fileName)
{
// All of the BitmapSource creation occurs in this method, keeping the calls to
// MemoryPressure.ProcessAdd() localized to this app domain
// Load the image from file
BitmapFrame bmpFrame = BitmapFrame.Create(new Uri(fileName));
int stride = bmpFrame.PixelWidth * bmpFrame.Format.BitsPerPixel;
byte[] pixels = new byte[bmpFrame.PixelHeight * stride];
// Construct and return the image information
bmpFrame.CopyPixels(pixels, stride, 0);
return new ImageLoaderProxyPixelData()
{
Pixels = pixels,
Stride = stride,
Rect = new Int32Rect(0, 0, bmpFrame.PixelWidth, bmpFrame.PixelHeight)
};
}
public void VirtualizingStackPanel_CleanUpVirtualizedItem(object sender, CleanUpVirtualizedItemEventArgs e)
{
// Get a reference to the WriteableBitmap before nullifying the property to release the reference
Thumbnail thumbnail = (Thumbnail)e.Value;
WriteableBitmap thumbnailImage = (WriteableBitmap)thumbnail.Image;
thumbnail.Image = null;
// Asynchronously add the WriteableBitmap back to the cache
Dispatcher.BeginInvoke((Action)(() =>
{
_writeableBitmapCache.Enqueue(thumbnailImage);
}), System.Windows.Threading.DispatcherPriority.Loaded);
}
}
// View-Model
public class Thumbnail : DependencyObject
{
private Func<Thumbnail, BitmapSource> _imageGetter;
private BitmapSource _image;
public Thumbnail(Func<Thumbnail, BitmapSource> imageGetter)
{
_imageGetter = imageGetter;
}
public string FilePath
{
get { return (string)GetValue(FilePathProperty); }
set { SetValue(FilePathProperty, value); }
}
public static readonly DependencyProperty FilePathProperty =
DependencyProperty.Register("FilePath", typeof(string), typeof(Thumbnail));
public BitmapSource Image
{
get
{
if (_image== null)
_image = _imageGetter(this);
return _image;
}
set { _image = value; }
}
}
public class ImageLoaderProxyPixelData
{
public byte[] Pixels { get; set; }
public Int32Rect Rect { get; set; }
public int Stride { get; set; }
}
}
As a benchmark, (for myself if no one else, I suppose) I've tested this approach on a 10-year old laptop with a Centrino processor and had almost no issue with fluidity in the UI.
I wish I could take credit for this, but I believe a better answer is already there: How can I prevent garbage collection from being called when calling ShowDialog on a xaml window?
Even from the code of the ProcessAdd method one can see that nothing gets executed if _totalMemory is small enough. So I think this code is much easier to use and with less side effects:
typeof(BitmapImage).Assembly
.GetType("MS.Internal.MemoryPressure")
.GetField("_totalMemory", BindingFlags.NonPublic | BindingFlags.Static)
.SetValue(null, Int64.MinValue / 2);
We need to understand, though, what the method is supposed to do, and the comment from the .NET source is pretty clear:
/// Avalon currently only tracks unmanaged memory pressure related to Images.
/// The implementation of this class exploits this by using a timer-based
/// tracking scheme. It assumes that the unmanaged memory it is tracking
/// is allocated in batches, held onto for a long time, and released all at once
/// We have profiled a variety of scenarios and found images do work this way
So my conclusion is that by disabling their code, you risk filling up your memory because of the way images are managed. However, since you know that the application you use is large and that it might need GC.Collect to be called, a very simple and safe fix would be for you to call it yourself, when you feel you can.
The code there tries to execute it every time the total memory used goes over a threshold, with a timer so it doesn't happen too often. That would be 30 seconds for them. So why don't you call GC.Collect(2) when you are closing forms or doing other things that would release the use of many images? Or when the computer is idle or the app is not in focus, etc?
I took the time to check where the _totalMemory value comes from and it seems that every time they create a WritableBitmap, they add the memory for it to _totalMemory, which is calculated here: http://referencesource.microsoft.com/PresentationCore/R/dca5f18570fed771.html as pixelWidth * pixelHeight * pixelFormat.InternalBitsPerPixel / 8 * 2; and further on in methods that work with Freezables. It is an internal mechanism to keep track of the memory allocated by the graphical representation of almost any WPF control.
It sounds to me as you could not only set _totalMemory to a very low value, but also hijack the mechanism. You could occasionally read the value, add to it the large value you subtracted initially, and get the actual value of memory used by drawn controls and decide whether you want to GC.Collect or not.
Wonder if anyone could explain what is going on with this weird little optimisation in our draw code. We replaced the first little bit of code with the second and got a huge speed increase (4400 tick -> 15 ticks using stopwatch class)
// Add all the visible sprites to the list
m_renderOrder.Clear();
foreach (CSpriteInternalData sprite in m_InternalData)
{
if (!sprite.m_bRender) continue;
m_renderOrder.Add(sprite);
}
Replaced with...
// Add all the visible sprites to the list
m_renderOrder.Clear();
renderOrderCount = 0;
for (int i = 0; i < m_numSprites; i++ )
{
if (m_InternalData[i].m_bRender)
m_renderOrder[renderOrderCount++] = m_InternalData[i];
}
I looks to be the simplest little change, for such a huge increase in speed. Can anyone help?
If CSpriteInternalData is a struct, i.e. a value type, each time when you assign a value of that type to a variable, a copy is done.
MyStruct a = new MyStruct(50);
MyStruct b = a; //a is copied to b;
a.Value = 10;
Console.WriteLine(b.Value); //still 10, has a separate copy of value
If structs are small and portable, that is not much of a problem, but if the structs are large, they can get slow. Foreach creates a variable that is repeatedly assigned a value from the collection, so if CSpriteInternalData is a struct, each one is in turn copied to the sprite variable, and that could take time.
Also, the line when you Add the item to the m_renderOrder collection, invokes another copy of the structure, but I guess only a few of them have the m_bRender flag set, so that one does not take too much time.
If that is the cause of the slowdown / speedup I would wholeheartedly recommend that you change CSpriteInternalData to a class, that would use reference behavior, and just copy references around, instead of whole copies.
foreach always creates an instance of an enumerator that is returned by GetEnumerator method and that enumerator also keeps state throughout the life cycle of the foreach loop.
It then repeatedly calls for the Next() object on the enumerator and runs your code for each object it returns.
You can create you own emulator in case. But better to user for loop instead when execution time matters.
The foreach loop has a slightly different purpose. It is meant for itterating through some collection that implements IEnumerable. It's performance is much slower.
for the reference you can see
http://www.codeproject.com/Articles/6759/FOREACH-Vs-FOR-C
Let's think of it as a family tree, a father has kids, those kids have kids, those kids have kids, etc...
So I have a recursive function that gets the father uses Recursion to get the children and for now just print them to debug output window...But at some point ( after one hour of letting it run and printing like 26000 rows) it gives me a StackOverFlowException.
So Am really running out of memory? hmmm? then shouldn't I get an "Out of memory exception"? on other posts I found people were saying if the number of recursive calls are too much, you might still get a SOF exception...
Anyway, my first thought was to break the tree into smaller sub-strees..so I know for a fact that my root father always has these five kids, so Instead of Calling my method one time with root passed to it, I said ok call it five times with Kids of root Passes to it.. It helped I think..but still one of them is so big - 26000 rows when it crashes - and still have this issue.
How about Application Domains and Creating new Processes at run time at some certain level of depth? Does that help?
How about creating my own Stack and using that instead of recursive methods? does that help?
here is also a high-level of my code, please take a look, maybe there is actually something silly wrong with this that causes SOF error:
private void MyLoadMethod(string conceptCKI)
{
// make some script calls to DB, so that moTargetConceptList2 will have Concept-Relations for the current node.
// when this is zero, it means its a leaf.
int numberofKids = moTargetConceptList2.ConceptReltns.Count();
if (numberofKids == 0)
return;
for (int i = 1; i <= numberofKids; i++)
{
oUCMRConceptReltn = moTargetConceptList2.ConceptReltns.get_ItemByIndex(i, false);
//Get the concept linked to the relation concept
if (oUCMRConceptReltn.SourceCKI == sConceptCKI)
{
oConcept = moTargetConceptList2.ItemByKeyConceptCKI(oUCMRConceptReltn.TargetCKI, false);
}
else
{
oConcept = moTargetConceptList2.ItemByKeyConceptCKI(oUCMRConceptReltn.SourceCKI, false);
}
//builder.AppendLine("\t" + oConcept.PrimaryCTerm.SourceString);
Debug.WriteLine(oConcept.PrimaryCTerm.SourceString);
MyLoadMethod(oConcept.ConceptCKI);
}
}
How about creating my own Stack and using that instead of recursive methods? does that help?
Yes!
When you instantiate a Stack<T> this will live on the heap and can grow arbitrarily large (until you run out of addressable memory).
If you use recursion you use the call stack. The call stack is much smaller than the heap. The default is 1 MB of call stack space per thread. Note this can be changed, but it's not advisable.
StackOverflowException is quite different to OutOfMemoryException.
OOME means that there is no memory available to the process at all. This could be upon trying to create a new thread with a new stack, or in trying to create a new object on the heap (and a few other cases).
SOE means that the thread's stack - by default 1M, though it can be set differently in thread creation or if the executable has a different default; hence ASP.NET threads have 256k as a default rather than 1M - was exhausted. This could be upon calling a method, or allocating a local.
When you call a function (method or property), the arguments of the call are placed on the stack, the address the function should return to when it returns are put on the stack, then execution jumps to the function called. Then some locals will be placed on the stack. Some more may be placed on it as the function continues to execute. stackalloc will also explicitly use some stack space where otherwise heap allocation would be used.
Then it calls another function, and the same happens again. Then that function returns, and execution jumps back to the stored return address, and the pointer within the stack moves back up (no need to clean up the values placed on the stack, they're just ignored now) and that space is available again.
If you use up that 1M of space, you get a StackOverflowException. Because 1M (or even 256k) is a large amount of memory for these such use (we don't put really large objects in the stack) the three things that are likely to cause an SOE are:
Someone thought it would be a good idea to optimise by using stackalloc when it wasn't, and they used up that 1M fast.
Someone thought it would be a good idea to optimise by creating a thread with a smaller than usual stack when it wasn't, and they use up that tiny stack.
A recursive (whether directly or through several steps) call falls into an infinite loop.
It wasn't quite infinite, but it was large enough.
You've got case 4. 1 and 2 are quite rare (and you need to be quite deliberate to risk them). Case 3 is by far the most common, and indicates a bug in that the recursion shouldn't be infinite, but a mistake means it is.
Ironically, in this case you should be glad you took the recursive approach rather than iterative - the SOE reveals the bug and where it is, while with an iterative approach you'd probably have an infinite loop bringing everything to a halt, and that can be harder to find.
Now for case 4, we've got two options. In the very very rare cases where we've got just slightly too many calls, we can run it on a thread with a larger stack. This doesn't apply to you.
Instead, you need to change from a recursive approach to an iterative one. Most of the time, this isn't very hard thought it can be fiddly. Instead of calling itself again, the method uses a loop. For example, consider the classic teaching-example of a factorial method:
private static int Fac(int n)
{
return n <= 1 ? 1 : n * Fac(n - 1);
}
Instead of using recursion we loop in the same method:
private static int Fac(int n)
{
int ret = 1;
for(int i = 1; i <= n, ++i)
ret *= i;
return ret;
}
You can see why there's less stack space here. The iterative version will also be faster 99% of the time. Now, imagine we accidentally call Fac(n) in the first, and leave out the ++i in the second - the equivalent bug in each, and it causes an SOE in the first and a program that never stops in the second.
For the sort of code you're talking about, where you keep producing more and more results as you go based on previous results, you can place the results you've got in a data-structure (Queue<T> and Stack<T> both serve well for a lot of cases) so the code becomes something like):
private void MyLoadMethod(string firstConceptCKI)
{
Queue<string> pendingItems = new Queue<string>();
pendingItems.Enqueue(firstConceptCKI);
while(pendingItems.Count != 0)
{
string conceptCKI = pendingItems.Dequeue();
// make some script calls to DB, so that moTargetConceptList2 will have Concept-Relations for the current node.
// when this is zero, it means its a leaf.
int numberofKids = moTargetConceptList2.ConceptReltns.Count();
for (int i = 1; i <= numberofKids; i++)
{
oUCMRConceptReltn = moTargetConceptList2.ConceptReltns.get_ItemByIndex(i, false);
//Get the concept linked to the relation concept
if (oUCMRConceptReltn.SourceCKI == sConceptCKI)
{
oConcept = moTargetConceptList2.ItemByKeyConceptCKI(oUCMRConceptReltn.TargetCKI, false);
}
else
{
oConcept = moTargetConceptList2.ItemByKeyConceptCKI(oUCMRConceptReltn.SourceCKI, false);
}
//builder.AppendLine("\t" + oConcept.PrimaryCTerm.SourceString);
Debug.WriteLine(oConcept.PrimaryCTerm.SourceString);
pendingItems.Enque(oConcept.ConceptCKI);
}
}
}
(I haven't completely checked this, just added the queuing instead of recursing to the code in your question).
This should then do more or less the same as your code, but iteratively. Hopefully that means it'll work. Note that there is a possible infinite loop in this code if the data you are retrieving has a loop. In that case this code will throw an exception when it fills the queue with far too much stuff to cope. You can either debug the source data, or use a HashSet to avoid enqueuing items that have already been processed.
Edit: Better add how to use a HashSet to catch duplicates. First set up a HashSet, this could just be:
HashSet<string> seen = new HashSet<string>();
Or if the strings are used case-insensitively, you'd be better with:
HashSet<string> seen = new HashSet<string>(StringComparison.InvariantCultureIgnoreCase) // or StringComparison.CurrentCultureIgnoreCase if that's closer to how the string is used in the rest of the code.
Then before you go to use the string (or perhaps before you go to add it to the queue, you have one of the following:
If duplicate strings shouldn't happen:
if(!seen.Add(conceptCKI))
throw new InvalidOperationException("Attempt to use \" + conceptCKI + "\" which was already seen.");
Or if duplicate strings are valid, and we just want to skip performing the second call:
if(!seen.Add(conceptCKI))
continue;//skip rest of loop, and move on to the next one.
I think you have a recursion's ring (infinite recursion), not a really stack overflow error. If you are got more memory for stack - you will get the overflow error too.
For test it:
Declare a global variable for storing a operable objects:
private Dictionary<int,object> _operableIds = new Dictionary<int,object>();
...
private void Start()
{
_operableIds.Clear();
Recurtion(start_id);
}
...
private void Recurtion(int object_id)
{
if(_operableIds.ContainsKey(object_id))
throw new Exception("Have a ring!");
else
_operableIds.Add(object_id, null/*or object*/);
...
Recurtion(other_id)
...
_operableIds.Remove(object_id);
}
Although I have been programming for about 11 years(mostly VB6, last 6 months C#), it's THE first time to actually ask a question :) I have found all my answers from teh interwebz but this issue i can't solve myself. Your site is among the most helpful places i have got the best answers from!
I will show the code i'm using (an extract of what's relevant). Problem is that when using RotateFlip method then the memory is increasing rapidly to ~200M and then get's collected by GC after some time. The main method calling it iterates about 30 times per second so the performance is of utmost importance here. I have tried using graphics matrix transform but this sometimes fails and shows non-flipped image. The application itself is based on using a webcam, hiding the preview, taking the callback picture and showing it in picturebox. It then overlays a rectangle on if from another class. That's the reason to use callback and not preview window.
Capture.cs class:
internal Bitmap LiveImage;
int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
{
LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer);
if (ExpImg) // local bool, used rarely when the picture saving is triggered
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
LiveImage.PixelFormat);
using (a)
a.Save("ocr.bmp", ImageFormat.Bmp);
}
else // dmnit, rotateflip leaks like h*ll but matrix transform doesn't sometimes flip :S
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
/*using (var g = Graphics.FromImage(LiveImage))
{
g.Transform = _mtx;
g.DrawImage(LiveImage, 0, 0);
}*/
}
GC.Collect(); // gotta use it with rotateflip, otherwise it gets crazy big, like ~200M :O
return 0;
}
}
In main form i have an event that's updating the picture in the picturebox:
private void SetPic()
{
pctCamera.Image = _cam.LiveImage;
_cam.PicIsFree = false;
}
Because i need to get the image to main form which is in another class then i figured the most logical is the exposed Bitmap which is updated on every callback frame.
The reason i don't want to use matrix transform is because it's slower and sometimes with this speed it fails to flip the image and the frequency of such behavior is quite different with different PC's with different hardware capabilities and CPU speeds, also the fastest framerate 30fps with a 1.2GHz CPU shows this very frequently.
So, can you help me to figure it out? I'm not actually using it in current version, i'm using the commented-out matrix transform because i feel bad for using GC.Collect :(
Thank You!!!
pctCamera.Image = _cam.LiveImage;
Heavy memory usage like you observe is a sure sign that you missed an opportunity to call Dispose() somewhere, letting the unmanaged resources (memory mostly) used by a bitmap get released early instead of letting the garbage collector do it. The quoted statement is one such case, you are not disposing the old image referenced by the picture box. Fix:
if (pctCamera.Image != null) pctCamera.Image.Dispose();
pctCamera.Image = _cam.LiveImage;
You can rewrite your code like this:
internal Bitmap LiveImage;
int ISampleGrabberCB.BufferCB(double bufferSize, IntPtr pBuffer, int bufferLen)
{
using (LiveImage = new Bitmap(_width, _height, _stride, PixelFormat.Format24bppRgb, pBuffer))
{
LiveImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
if (ExpImg) // local bool, used rarely when the picture saving is triggered
{
var a = LiveImage.Clone(new Rectangle(Currect.Left, Currect.Top, Currect.Width, Currect.Height),
LiveImage.PixelFormat);
using (a)
a.Save("ocr.bmp", ImageFormat.Bmp);
}
}
return 0;
}
Bitmap is an Image class, and implements the IDispose. As you create Bitmap each time, I suggest to use using statement for automatically freeing the resources.
GC.Collect is there for this situation. Collecting the data is the ONLY way to free it and when creating HUGE bitmaps its the way to go. Does a GC.Collect really slow things down?
Other then that you should keep the number of bitmap copies as low as possible.
It's possible to determine memory usage (according to Jon Skeet's blog)
like this :
public class Program
{
private static void Main()
{
var before = GC.GetTotalMemory(true);
var point = new Point(1, 0);
var after = GC.GetTotalMemory(true);
Console.WriteLine("Memory used: {0} bytes", after - before);
}
#region Nested type: Point
private class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
X = x;
Y = y;
}
}
#endregion
}
It prints Memory used: 16 bytes (I'm running x64 machine).
Consider we change Point declaration from class to struct. How then to determine memory used? Is is possible at all? I was unable to find anything about getting stack size in .NET
P.S
Yes, when changed to 'struct', Point instances will often be stored on Stack(not always), instead of Heap.Sorry for not posting it first time together with the question.
P.P.S
This situation has no practical usage at all(IMHO), It's just interesting for me whether it is possible to get Stack(short term storage) size. I was unable to find any info about it, so asked you, SO experts).
You won't see a change in GetTotalMemory if you create the struct the way you did, since it's going to be part of the thread's stack, and not allocated separately. The GetTotalMemory call will still work, and show you the total allocation size of your process, but the struct will not cause new memory to be allocated.
You can use sizeof(Type) or Marshal.SizeOf to return the size of a struct (in this case, 8 bytes).
There is special CPU register, ESP, that contains pointer to the top of the stack. Probably you can find a way to read this register from .Net (using some unsafe or external code). Then just compare value of this pointer at given moment with value at thread start - and difference between them will be more or less acurate amount of memory, used for thread's stack. Not sure if it really works, just an idea :)
In isolation, as you have done here, you might have a "reasonable" amount of success with this methodology. I am not confident the information is useful, but running this methodology, especially if you run it numerous times to ensure you did not have any other piece of code or GC action affecting the outcome. Utilizing this methodology in a real world application is less likely to give accurate results, however, as there are too many variables.
But realize, this only "reasonable" and not a surety.
Why do you need to know the size of objects? Just curious, as knowing the business reason may lead to other alternatives.