Skip to content

Commit

Permalink
[android] avoid View.Context where unecessary
Browse files Browse the repository at this point in the history
Context: dotnet#7996
Context: https://github.com/unoplatform/performance/tree/master/src/dopes/DopeTestMaui

Building upon my changes in dotnet#7996 (sample sample), I noticed:

    7.60s (14%) mono.android!Android.Views.View.get_Context()

14% of the time is literally spent calling `View.Context`!

We did a little investigation on the underlying Java interop, this is
basically doing:

1. Call into JNI, get an `IntPtr`.
2. See if that `IntPtr` maps to a C# object that is already alive.
3. Return the C# object, or create a new one if needed.

We can actually avoid all this work, in this case.

For example:

    5.48s (10%) microsoft.maui!Microsoft.Maui.Platform.TransformationExtensions.UpdateAnchorX(Android.Views.View,Microsoft.Maui.IView)
    4.28s (8.2%) microsoft.maui!Microsoft.Maui.Platform.TransformationExtensions.UpdateAnchorY(Android.Views.View,Microsoft.Maui.IView)

These extension methods call `View.Context` twice:

    public static void UpdateTranslationY(this AView platformView, IView view)
    {
        if (platformView.Context == null)
            return;
        platformView.TranslationY = platformView.Context.ToPixels(view.TranslationY);
    }

We can actually, make an overload for `ToPixels()` to where
`View.Context` wouldn't be called *at all*:

    internal static float ToPixels (this View view, double dp)
    {
        if (s_displayDensity != float.MinValue)
            return (float)Math.Ceiling(dp * s_displayDensity);
        return view.Context.ToPixels(dp);
    }

I used this everywhere I saw it appearing in `dotnet trace` output.

Next, I saw `View.Context` being called a lot from `LayoutViewGroup`
and `ContentViewGroup`. I could simply store the value in the
constructor for these types, make it non-nullable, and remove null
checks.

~~ Results ~~

A `Release` build on a Pixel 5 device, I was getting:

    Before: 64.23 Dopes/s
    After:  81.70 Dopes/s
  • Loading branch information
jonathanpeppers committed Jun 13, 2022
1 parent 40f40e7 commit 47042e4
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 60 deletions.
27 changes: 15 additions & 12 deletions src/Core/src/Platform/Android/ContentViewGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,33 @@ namespace Microsoft.Maui.Platform
public class ContentViewGroup : ViewGroup
{
IBorderStroke? _clip;
readonly Context _context;

public ContentViewGroup(Context context) : base(context)
{
_context = context;
}

public ContentViewGroup(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
var context = Context;
ArgumentNullException.ThrowIfNull(context);
_context = context;
}

public ContentViewGroup(Context context, IAttributeSet attrs) : base(context, attrs)
{
_context = context;
}

public ContentViewGroup(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
_context = context;
}

public ContentViewGroup(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
_context = context;
}

protected override void DispatchDraw(Canvas? canvas)
Expand All @@ -43,19 +51,14 @@ protected override void DispatchDraw(Canvas? canvas)

protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (Context == null)
{
return;
}

if (CrossPlatformMeasure == null)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}

var deviceIndependentWidth = widthMeasureSpec.ToDouble(Context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(Context);
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);

var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
Expand All @@ -67,8 +70,8 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;

var platformWidth = Context.ToPixels(width);
var platformHeight = Context.ToPixels(height);
var platformWidth = _context.ToPixels(width);
var platformHeight = _context.ToPixels(height);

// Minimum values win over everything
platformWidth = Math.Max(MinimumWidth, platformWidth);
Expand All @@ -79,12 +82,12 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)

protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
{
if (CrossPlatformArrange == null || Context == null)
if (CrossPlatformArrange == null)
{
return;
}

var destination = Context!.ToCrossPlatformRectInReferenceFrame(left, top, right, bottom);
var destination = _context.ToCrossPlatformRectInReferenceFrame(left, top, right, bottom);

CrossPlatformArrange(destination);
}
Expand All @@ -107,7 +110,7 @@ void ClipChild(Canvas? canvas)
if (Clip == null || canvas == null)
return;

float density = Context.GetDisplayDensity();
float density = _context.GetDisplayDensity();

float strokeThickness = (float)Clip.StrokeThickness;
float offset = strokeThickness / 2;
Expand Down
7 changes: 7 additions & 0 deletions src/Core/src/Platform/Android/ContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ public static void ShowKeyboard(this Context self, global::Android.Views.View vi
service.ShowSoftInput(view, ShowFlags.Implicit);
}

internal static float ToPixels (this View view, double dp)
{
if (s_displayDensity != float.MinValue)
return (float)Math.Ceiling(dp * s_displayDensity);
return view.Context.ToPixels(dp);
}

public static float ToPixels(this Context? self, double dp)
{
EnsureMetrics(self);
Expand Down
25 changes: 14 additions & 11 deletions src/Core/src/Platform/Android/LayoutViewGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,35 @@ namespace Microsoft.Maui.Platform
public class LayoutViewGroup : ViewGroup
{
readonly ARect _clipRect = new();
readonly Context _context;

public bool InputTransparent { get; set; }

public LayoutViewGroup(Context context) : base(context)
{
_context = context;
}

public LayoutViewGroup(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
var context = Context;
ArgumentNullException.ThrowIfNull(context);
_context = context;
}

public LayoutViewGroup(Context context, IAttributeSet attrs) : base(context, attrs)
{
_context = context;
}

public LayoutViewGroup(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
_context = context;
}

public LayoutViewGroup(Context context, IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
_context = context;
}

public bool ClipsToBounds { get; set; }
Expand All @@ -44,19 +52,14 @@ public LayoutViewGroup(Context context, IAttributeSet attrs, int defStyleAttr, i
// apply to ViewHandlerExtensions.MeasureVirtualView
protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
if (Context == null)
{
return;
}

if (CrossPlatformMeasure == null)
{
base.OnMeasure(widthMeasureSpec, heightMeasureSpec);
return;
}

var deviceIndependentWidth = widthMeasureSpec.ToDouble(Context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(Context);
var deviceIndependentWidth = widthMeasureSpec.ToDouble(_context);
var deviceIndependentHeight = heightMeasureSpec.ToDouble(_context);

var widthMode = MeasureSpec.GetMode(widthMeasureSpec);
var heightMode = MeasureSpec.GetMode(heightMeasureSpec);
Expand All @@ -68,8 +71,8 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
var width = widthMode == MeasureSpecMode.Exactly ? deviceIndependentWidth : measure.Width;
var height = heightMode == MeasureSpecMode.Exactly ? deviceIndependentHeight : measure.Height;

var platformWidth = Context.ToPixels(width);
var platformHeight = Context.ToPixels(height);
var platformWidth = _context.ToPixels(width);
var platformHeight = _context.ToPixels(height);

// Minimum values win over everything
platformWidth = Math.Max(MinimumWidth, platformWidth);
Expand All @@ -83,12 +86,12 @@ protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
// apply to ViewHandlerExtensions.MeasureVirtualView
protected override void OnLayout(bool changed, int l, int t, int r, int b)
{
if (CrossPlatformArrange == null || Context == null)
if (CrossPlatformArrange == null || _context == null)
{
return;
}

var destination = Context!.ToCrossPlatformRectInReferenceFrame(l, t, r, b);
var destination = _context.ToCrossPlatformRectInReferenceFrame(l, t, r, b);

CrossPlatformArrange(destination);

Expand Down
15 changes: 4 additions & 11 deletions src/Core/src/Platform/Android/TextViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,11 @@ public static void UpdateVerticalTextAlignment(this TextView textView, ITextAlig

public static void UpdatePadding(this TextView textView, ILabel label)
{
var context = textView.Context;

if (context == null)
{
return;
}

textView.SetPadding(
(int)context.ToPixels(label.Padding.Left),
(int)context.ToPixels(label.Padding.Top),
(int)context.ToPixels(label.Padding.Right),
(int)context.ToPixels(label.Padding.Bottom));
(int)textView.ToPixels(label.Padding.Left),
(int)textView.ToPixels(label.Padding.Top),
(int)textView.ToPixels(label.Padding.Right),
(int)textView.ToPixels(label.Padding.Bottom));
}

public static void UpdateTextDecorations(this TextView textView, ILabel label)
Expand Down
20 changes: 4 additions & 16 deletions src/Core/src/Platform/Android/TransformationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,12 @@ public static class TransformationExtensions
{
public static void UpdateTranslationX(this AView platformView, IView view)
{
if (platformView.Context == null)
return;

platformView.TranslationX = platformView.Context.ToPixels(view.TranslationX);
platformView.TranslationX = platformView.ToPixels(view.TranslationX);
}

public static void UpdateTranslationY(this AView platformView, IView view)
{
if (platformView.Context == null)
return;

platformView.TranslationY = platformView.Context.ToPixels(view.TranslationY);
platformView.TranslationY = platformView.ToPixels(view.TranslationY);
}

public static void UpdateScale(this AView platformView, IView view)
Expand Down Expand Up @@ -63,19 +57,13 @@ public static void UpdateRotationY(this AView platformView, IView view)

public static void UpdateAnchorX(this AView platformView, IView view)
{
if (platformView.Context == null)
return;

var pivotX = (float)(view.AnchorX * platformView.Context.ToPixels(view.Frame.Width));
var pivotX = (float)(view.AnchorX * platformView.ToPixels(view.Frame.Width));
PlatformInterop.SetPivotXIfNeeded(platformView, pivotX);
}

public static void UpdateAnchorY(this AView platformView, IView view)
{
if (platformView.Context == null)
return;

var pivotY = (float)(view.AnchorY * platformView.Context.ToPixels(view.Frame.Height));
var pivotY = (float)(view.AnchorY * platformView.ToPixels(view.Frame.Height));
PlatformInterop.SetPivotYIfNeeded(platformView, pivotY);
}
}
Expand Down
16 changes: 6 additions & 10 deletions src/Core/src/Platform/Android/ViewExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,8 @@ public static partial class ViewExtensions
{
public static void Initialize(this AView platformView, IView view)
{
var context = platformView.Context;
if (context == null)
return;

var pivotX = (float)(view.AnchorX * context.ToPixels(view.Frame.Width));
var pivotY = (float)(view.AnchorY * context.ToPixels(view.Frame.Height));
var pivotX = (float)(view.AnchorX * platformView.ToPixels(view.Frame.Width));
var pivotY = (float)(view.AnchorY * platformView.ToPixels(view.Frame.Height));
int visibility;

if (view is IActivityIndicator a)
Expand All @@ -43,12 +39,12 @@ public static void Initialize(this AView platformView, IView view)
PlatformInterop.Set(platformView,
visibility: visibility,
layoutDirection: (int)GetLayoutDirection(view),
minimumHeight: (int)context.ToPixels(view.MinimumHeight),
minimumWidth: (int)context.ToPixels(view.MinimumWidth),
minimumHeight: (int)platformView.ToPixels(view.MinimumHeight),
minimumWidth: (int)platformView.ToPixels(view.MinimumWidth),
enabled: view.IsEnabled,
alpha: (float)view.Opacity,
translationX: context.ToPixels(view.TranslationX),
translationY: context.ToPixels(view.TranslationY),
translationX: platformView.ToPixels(view.TranslationX),
translationY: platformView.ToPixels(view.TranslationY),
scaleX: (float)(view.Scale * view.ScaleX),
scaleY: (float)(view.Scale * view.ScaleY),
rotation: (float)view.Rotation,
Expand Down

0 comments on commit 47042e4

Please sign in to comment.