Bug 23822 - [Performance] Xamarin.Forms is slow when adding labels to the layout on iOS and Android
Summary: [Performance] Xamarin.Forms is slow when adding labels to the layout on iOS a...
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Forms ()
Version: 1.2.3
Hardware: PC Mac OS
: --- enhancement
Target Milestone: ---
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2014-10-14 14:16 UTC by Prashant Cholachagudda
Modified: 2016-07-02 22:56 UTC (History)
21 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED FIXED

Description Prashant Cholachagudda 2014-10-14 14:16:12 UTC
Xamarin.Forms takes about 300ms on Android to add the 40 labels on Android and about 100ms on iOS.
On Xamarin.Android and Xamarin.iOS it take roughly 20ms.

Xamarin.Forms testcase: https://github.com/perpetual-mobile/XFormsPerformance


Xamarin.Android testcase:
using System;

using Android.App;
using Android.Widget;
using Android.OS;
using System.Diagnostics;

namespace LabelTest
{
	[Activity (Label = "LabelTest", MainLauncher = true, Icon = "@drawable/icon")]
	public class MainActivity : Activity
	{
		readonly Stopwatch timer = new Stopwatch();

		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			// Set our view from the "main" layout resource
			SetContentView (Resource.Layout.Main);


			Button button = FindViewById<Button> (Resource.Id.myButton);
			var layout = FindViewById<LinearLayout> (Resource.Id.myLayout);
			
			button.Click += delegate {
				timer.Start();

				for (int i = 0; i < 40; i++) {
					var label = new TextView(this);
					label.Text = string.Format("Label {0}", i);
					layout.AddView(label);
				}

				timer.Stop();

				Console.WriteLine ("Elapsed {0}", timer.ElapsedMilliseconds);
			};
		}
	}
}
Comment 1 Rodja Trappe 2014-10-24 04:35:53 UTC
The issue is also fully described on StackOverflow: http://stackoverflow.com/questions/26364509/why-is-xamarin-forms-so-slow-when-displaying-a-few-labels-especially-on-android
Comment 2 Rodja Trappe 2015-04-28 04:19:31 UTC
Are you working on this crucial issue right now? It makes almost all Xamarin.Forms Apps incredible slow and hence brings an overall bad user experience.

I've just tested my example App from https://github.com/perpetual-mobile/XFormsPerformance with Xamarin.Forms version 1.4.2.6355 and could not see any improvements over version 1.2.3.
Comment 3 Miha Markic 2015-04-28 04:31:27 UTC
Let me just add the fact that labels on WinPhone are in their onw league of slowness.
Comment 4 Ed Sahakian 2015-05-05 13:18:19 UTC
I have the same issues... In Forms I some complicated layouts sometime and the initial setup of the page takes a few seconds sometime and much worse on slower devices...
Comment 5 Evgheni Obrucicov 2015-05-07 10:37:03 UTC
The default state of layouting code even in 1.4.3-pre2 has the same problems as in 1.2.3. And the problem is that anytime a child is invalidated all other siblings are remeasured and relayout. So when you add 40 label you 40 times relayouting all the labels that was added in previous cycle iteration. Fortunately in 1.3.0 we were given some control over this behavior when subclassing a layout.
For my project i needed to have a dynamic grid that shows pairs of text and value labels  as a row. So i created a test app that clears a grid and dynamical add 2x20 labels to a grid on a button press. Results were:

IOS (only adding labels): ~450ms
IOS (delete all labels and then add them again): ~1900ms
Android(only adding labels): ~700ms
Android(delete all labels and then add them again): ~2600ms

Then i created my own layout that add all children and then measure and layout them all at once.
Result were 50-90 ms in all scenarios for both Andorid and IOS.

For more info about what 1.3.0 added look here http://ericsink.com/entries/xf_13_better.html

I really hope layouts in XF will get more intelligent by default because now creating anything complex or dynamic require building your own layouts.
Comment 6 Falko Schindler 2015-05-07 12:07:48 UTC
How did you create such a fast layout (50-90ms)? When I try to use a "QuickStackLayout" as shown below, the speed improvement is less than 10 %.

    public class QuickStackLayout : StackLayout
    {
        protected override bool ShouldInvalidateOnChildAdded(View child)
        {
            return false;
        }

        protected override bool ShouldInvalidateOnChildRemoved(View child)
        {
            return false;
        }

        protected override void OnChildMeasureInvalidated()
        {
        }
    }
Comment 7 Ed Sahakian 2015-05-07 13:21:18 UTC
Yes, it would be very nice to see a full working example of an improved layout... The article at  http://ericsink.com/entries/xf_13_better.html is a great start but does not show a working sample...
Comment 8 Evgheni Obrucicov 2015-05-07 13:52:25 UTC
I didn't make any universal layouts that can be reused, like QuickStackLayout or something. I made an layout for children composition i needed for this specific control only.
I add all children to my custom layout subclassed from Layout<View>, this doesn't cause any display update tnx to the code you mentioned, and then i call LayoutAllChildren(ScreenWidth) that looks like this (MargineW and ValueW are constants):

    protected override void LayoutAllChildren(double width)
    {

        double TextW = width - 2 - MargineW - ValueW;
        double ControlHeight = 0;
        
        for (int i = 0; i < Children.Count; i += 3)
        {

            var RowH = measurer.GetHeight(((Label) Children[i + 1]).Text, TextW);
            Rectangle rmargine = new Rectangle(0, ControlHeight, MargineW, RowH);
            Rectangle rtext = new Rectangle(MargineW, ControlHeight, TextW, RowH);
            Rectangle rvalue = new Rectangle(MargineW + TextW + 1, ControlHeight, ValueW, RowH);
            Children[i].Layout(rmargine);
            Children[i + 1].Layout(rtext);
            Children[i + 2].Layout(rvalue);
            ControlHeight += RowH + 1;
        }
        HeightRequest = ControlHeight - 1;
    }



Where measurer is my interface for DependencyService
For IOS it's implementation looks like this:


        public double GetHeight(string text,double width)
        {
            return ((NSString)text).StringSize(UIFont.FromName(StaticFontName, StaticFontSize), new GSize((float)width,float.MaxValue), UILineBreakMode.WordWrap).Height+10;

        }

And for Android implementation is:

        public double GetHeight(string Text, double width)
        {
            var textView = new TextView(Forms.Context);
            textView.Text = Text;
            textView.SetTextSize(ComplexUnitType.Px, StaticFontSize);
            int widthMeasureSpec = View.MeasureSpec.MakeMeasureSpec((int)width, MeasureSpecMode.AtMost);
            int heightMeasureSpec =View.MeasureSpec.MakeMeasureSpec(0, MeasureSpecMode.Unspecified);
            textView.Measure(widthMeasureSpec, heightMeasureSpec);
            return textView.MeasuredHeight;
        }

This create a grid with a row:  text with left margine 10px | value and row height is determined based on text length.

If you have any more questions pls pm me at http://forums.xamarin.com/profile/77086/Xavyer
Comment 9 Cody Beyer (MSFT) 2015-05-12 17:14:05 UTC
Confirmed using the sample in Comment 0
Comment 10 Rodja Trappe 2015-06-11 15:18:02 UTC
Why is this issue's importance marked as "enhancement"? It surly is a critical bug to have such a slow rendering experience. Developers using Xamarin.Forms  crave for this: 3250 visits on the StackOverflow question "Why is Xamarin.Forms so slow when displaying a few labels (especially on Android)?". http://stackoverflow.com/questions/26364509/why-is-xamarin-forms-so-slow-when-displaying-a-few-labels-especially-on-android.

Could you give us an estimate when you think about working on this issue. We are still holding off releases.
Comment 11 aed 2015-06-18 02:43:29 UTC
Not sure if there are other bugs tracking perf but I must agree with Rodja on the importance of this issue. This bug is degrading our performance and we can't even get decent performance on the most powerful of devices (e.g. Nexus 6), despite doing lots of custom rendering and various hacks and OnSizeRequest overrides to try and make things faster.
Comment 12 Ed Sahakian 2015-06-20 19:07:44 UTC
I have basically given up waiting for the layout to be faster... And I am writing my own basic layout engine to get the job done... Not sure if it will be faster than than using a bunch of StackLayouts but I imagine it would be... 

For now my app will be released using the slower layout since I cant' delay the launch but if my new basic layout is a hit then I will update key screens of my app with my new layout system... I have to see how many types of layout features I actually need to get the job done and stick to a "less is more" approach...

And off-course I will share the layout code with everyone...
Comment 13 Jason Smith [MSFT] 2016-03-18 08:37:53 UTC
We have made large improvements to this performance case, I am closing this report in particular because it no longer serves the same purpose it used to.
Comment 14 aed 2016-07-02 22:56:20 UTC
It's worth noting that while perf improved a lot for Android with this, it tends to be at least 2x slower on Android than on iOS for more complex layouts, on comparable phones. Not sure if you have a bug tracking this.