-
- KeldorKatarn
- Posts: 243
- Joined:
Help with avoiding memory allocations
Hi, I need some help in the hope to get rid of some memory allocations, if possible.
I see some happening here during measure and arrange and I'm not sure what's causing them:
Might be related to this custom panel (just there to overlay things and meant to be more lightweight than Grid)
Also I see some more allocations here related to PropertyChanged. Is this a boxing issue?
I have a notifying property of type Bracket, which is a Struct containing a Point, a Size and a bool.
Am I seeing these GetPropertyValue allocations because the structs get boxed? Can I avoid that somehow?
Also, I'm guessing feeding a Value Type into a IValueConverter will also always lead to boxing? For WPF it's fine but I'd love to avoid them in Unity if at all possible.
Also what's the Dictionary being used here?
I see some happening here during measure and arrange and I'm not sure what's causing them:
Might be related to this custom panel (just there to overlay things and meant to be more lightweight than Grid)
Code: Select all
/// <summary>A panel that stacks all its children on top of each other, letting them overlay each other.</summary>
public class OverlayStackPanel : Panel
{
public OverlayStackPanel()
{
Loaded += OnLoaded;
}
#if UNITY_5_3_OR_NEWER
private List<UIElement> InternalChildren { get; set; } = null;
#endif
/// <inheritdoc/>
protected override Size MeasureOverride(Size availableSize)
{
if (InternalChildren is null)
{
return new Size(0, 0);
}
double maxWidth = 0;
double maxHeight = 0;
for (var index = 0; index < InternalChildren.Count; index++)
{
var child = InternalChildren[index];
child?.Measure(availableSize);
if (child is null)
{
continue;
}
var childDesiredSize = child.DesiredSize;
maxWidth = Math.Max(maxWidth, childDesiredSize.Width);
maxHeight = Math.Max(maxHeight, childDesiredSize.Height);
}
// Panel size matches the largest child's size
return new Size((float)maxWidth, (float)maxHeight);
}
/// <inheritdoc/>
protected override Size ArrangeOverride(Size finalSize)
{
if (InternalChildren is null)
{
return finalSize;
}
// Arrange each child to occupy the same space (stacked on top of each other)
for (var index = 0; index < InternalChildren.Count; index++)
{
var child = InternalChildren[index];
// Positioning each child at (0, 0) ensures they are stacked on top of each other
child?.Arrange(new Rect(0, 0, finalSize.Width, finalSize.Height));
}
return finalSize;
}
private void OnLoaded(object sender, RoutedEventArgs args)
{
#if UNITY_5_3_OR_NEWER
// Cache children since calling Children in a loop seems to allocate
InternalChildren = Children.ToList();
#endif
Loaded -= OnLoaded;
}
}
Am I seeing these GetPropertyValue allocations because the structs get boxed? Can I avoid that somehow?
Also, I'm guessing feeding a Value Type into a IValueConverter will also always lead to boxing? For WPF it's fine but I'd love to avoid them in Unity if at all possible.
Also what's the Dictionary being used here?
Re: Help with avoiding memory allocations
We haven't forgotten about this post. However, the analysis requires time, and the team is quite busy preparing the first beta for Studio.
We will get back to this very soon.
We will get back to this very soon.
-
- KeldorKatarn
- Posts: 243
- Joined:
Re: Help with avoiding memory allocations
I found out by now that caching the children in OnLoaded caused a bug. But that's besides the point. I still couldn't tell where all those allocations came from. For now it's not a major issue I'm still in pre-production. But I'd like to hunt those down eventually.
Re: Help with avoiding memory allocations
Could you please create a ticket for this?
-
- KeldorKatarn
- Posts: 243
- Joined:
Re: Help with avoiding memory allocations
It's mainly this issue: https://www.noesisengine.com/bugs/view.php?id=3970Could you please create a ticket for this?
The boxing with value converters is a bit of a pain. There's no way to support generic Value converters I assume because that breaks compatibility with WPF?
-
-
sfernandez
Site Admin
- Posts: 3363
- Joined:
Re: Help with avoiding memory allocations
What do you mean by generic value converters?
The Binding only allows to assign an IValueConverter, which has an API that works with object values for input and output. I don't think there is much we can do about it without breaking user code.
The Binding only allows to assign an IValueConverter, which has an API that works with object values for input and output. I don't think there is much we can do about it without breaking user code.
-
- KeldorKatarn
- Posts: 243
- Joined:
Re: Help with avoiding memory allocations
I'm aware. unfortunately one of the bad legacy design remnants of WPF before generics were widely used to avoid boxing.What do you mean by generic value converters?
The Binding only allows to assign an IValueConverter, which has an API that works with object values for input and output. I don't think there is much we can do about it without breaking user code.
I've since moved a lot of code that used value converters into specialized controls. Triggering value conversion every frame is isn't a good option.
Re: Help with avoiding memory allocations
Even with generics (which I’m not sure how they would be used in XAML), boxing is still needed, or at least, I don’t currently see a way to avoid it.
In C++, we pool boxed values, making the process quite fast without significant extra memory allocation. Shouldn't C# be doing something similar? Have you measured the cost of boxing on your platform?
In C++, we pool boxed values, making the process quite fast without significant extra memory allocation. Shouldn't C# be doing something similar? Have you measured the cost of boxing on your platform?
-
- KeldorKatarn
- Posts: 243
- Joined:
Re: Help with avoiding memory allocations
I regards to issue https://www.noesisengine.com/bugs/view.php?id=3970
I think the problem are the delegate parameters of the methods in the Extend class. These seem to cause new delegates being allocated every time one of these is called. Which is every frame if you override one of them, which can be OnRender, GetVisualChild and others. Evertime I override any of these, I see the allocation happen, even if I leave my code empty. And the profiler says they happen outside of my code, so in the class that calls my code, with is the Extend class:
^It's these types of callback delegates I think that are allocated. Can those be cached in any way? Those seem to be the methods that get called if I use base.Foo for whatever I'm overriding. I don't even use the base class methods.
They get assigned here for example:
I think the problem are the delegate parameters of the methods in the Extend class. These seem to cause new delegates being allocated every time one of these is called. Which is every frame if you override one of them, which can be OnRender, GetVisualChild and others. Evertime I override any of these, I see the allocation happen, even if I leave my code empty. And the profiler says they happen outside of my code, so in the class that calls my code, with is the Extend class:
Code: Select all
private static IntPtr VisualGetChild(IntPtr cPtr, int index,
Visual.ChildrenCountBaseCallback countCallback, Visual.GetChildBaseCallback childCallback)They get assigned here for example:
Code: Select all
private static IntPtr VisualGetChild(IntPtr cPtr, int index,
Visual.ChildrenCountBaseCallback countCallback, Visual.GetChildBaseCallback childCallback) // <<-- Allocations seems to happen here at the native-managed border
{
try
{
Visual visual = (Visual)GetExtendInstance(cPtr);
if (visual != null)
{
visual.ChildrenCountBase = countCallback; //<<-- Assigned here
visual.GetChildBase = childCallback;
Visual child = visual.GetVisualChild(index); // <<-- My override code gets called
visual.GetChildBase = null;
visual.ChildrenCountBase = null;
// [...]
-
-
sfernandez
Site Admin
- Posts: 3363
- Joined:
Re: Help with avoiding memory allocations
Thanks for taking the time to isolate this issue.
It looks like PInvoke marshalling automatically allocates those delegates when transitioning from C++ to C#. We'll try to find a way to avoid it but it doesn't look easy to do.
It looks like PInvoke marshalling automatically allocates those delegates when transitioning from C++ to C#. We'll try to find a way to avoid it but it doesn't look easy to do.
Who is online
Users browsing this forum: Google [Bot] and 4 guests