You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@flex.apache.org by Harbs <ha...@gmail.com> on 2016/07/26 08:40:57 UTC

[FlexJS] Javascript efficient code patterns

I noticed a couple of things:
1. There’s lots of String(val) casts in FlexJS code. This practice is considered “bad” practice in Javascript where implicit conversion is generally quicker. So in a case where a number can be converted implicitly, the cast should be completely skipped and even when a number needs to be converted to a string, “” + val is more efficient than String(val). This is especially true in FlexJS where (I believe) such code will result in org.apache.flex.Language.as(val,”String”).

2. String concatenation: I’m not sure how much of an issue this is in current browsers, but using aray.join() to build a string was MUCH faster than building strings using the plus operator. For longer strings the difference was dramatic.

I just did a quick test in Chrome using the following functions:
function timePlusEquals(){
	var start = new Date();
	var i=-1;
	var len  = 100000;
	var str = "";
	while(++i<len)
	{
		str+= "a";
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
}
function timePlus(){
	var start = new Date();
	var i=-1;
	var len  = 100000;
	var str = "";
	while(++i<len)
	{
		str= str+"a";
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
}
function timeJoin(){
	var start = new Date();
	var i=-1;
	var len  = 100000;
	var arr = [];
	while(++i<len)
	{
		arr.push("a");
	}
	var str = arr.join("");
	var end = new Date();
	console.log(end.getTime()-start.getTime());
}

and these were my results:
timePlusEquals()
20
timePlus()
17
timeJoin()
7

Interestingly enough, increasing the string size to “abced” had very little effect on performance. Also interesting was that running the functions multiple times sequentially knocked the time down to between 1 and 3 ms for all functions. It looks like Chrome has some pretty good optimizations in place.

In Firefox it was interesting to note that “plusEquals was the fastest in all my tests. It took about 1! ms to complete. join() was the slowest at 3 ms. Increasing the number of iterations to 10000000 brought join() into the lead:
timePlusEquals()
440
timePlus()
413
timeJoin()
238

But Chrome was reversed (and again slower than Firefox).

I think the takeaway (at least for Chrome and Firefox) is that this is not the issue I thought it was.

However, string casting does seem to be an issue (at least a small one):

function stringCast1(){
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var str = "";
	while(++i<len)
	{
		str= String(188);
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};
function stringCast2(){
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var str = "";
	while(++i<len)
	{
		str= ""+188;
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};

In Firefox:
stringCast1()
3874
stringCast2()
4

That’s pretty dramatic.

In Chrome, less so; but still:
stringCast1()
63
stringCast2()
11

Re: [FlexJS] Javascript efficient code patterns

Posted by Alex Harui <ah...@adobe.com>.

On 7/26/16, 12:49 PM, "Harbs" <ha...@gmail.com> wrote:

>
>On Jul 26, 2016, at 10:39 PM, Alex Harui <ah...@adobe.com> wrote:
>
>> 
>> 
>> On 7/26/16, 11:39 AM, "Harbs" <ha...@gmail.com> wrote:
>> 
>>> 
>>> On Jul 26, 2016, at 8:15 PM, Alex Harui <ah...@adobe.com> wrote:
>>> 
>>>> 
>>>> 
>>>> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
>>>> 
>>>>> I noticed a couple of things:
>>>>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
>>>>> considered “bad” practice in Javascript where implicit conversion is
>>>>> generally quicker. So in a case where a number can be converted
>>>>> implicitly, the cast should be completely skipped and even when a
>>>>> number
>>>>> needs to be converted to a string, “” + val is more efficient than
>>>>> String(val). This is especially true in FlexJS where (I believe) such
>>>>> code will result in org.apache.flex.Language.as(val,”String”).
>>>> 
>>>> I'm not seeing this.  What source code is generating String(val)
>>>>calls?
>>>> Regarding optimization, do we know if GCC will do this ("" + val)
>>>> optimization for us?
>>>> 
>>>> In general, there is a big TODO around type conversions.
>>> 
>>> Good question. I just checked and there’s both String() and toString()
>>> calls in the optimized code. One example:
>>> return RA+String(a)+bc+String(this.alpha)}; It’s coming from one of the
>>> Graphic classes.
>> 
>> It would save me time to not have to search through the code for test
>> cases if you have them handy.
>
>I already replaced this one. Are you saying you are thinking of doing
>this replacement in the compiler? I’m not sure it’s really necessary.
>Implicit string conversions should be fine in Flash as well.

I guess I misunderstood.  I thought you were saying the generated code had
un-wanted String() calls.  If it is in the source itself, then that's fine
if you are sure the implicit conversion will "do the right thing".  I've
been burned enough that I probably am the person that adds these
unnecessary calls.

-Alex


Re: [FlexJS] Javascript efficient code patterns

Posted by Harbs <ha...@gmail.com>.
On Jul 26, 2016, at 10:39 PM, Alex Harui <ah...@adobe.com> wrote:

> 
> 
> On 7/26/16, 11:39 AM, "Harbs" <ha...@gmail.com> wrote:
> 
>> 
>> On Jul 26, 2016, at 8:15 PM, Alex Harui <ah...@adobe.com> wrote:
>> 
>>> 
>>> 
>>> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
>>> 
>>>> I noticed a couple of things:
>>>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
>>>> considered “bad” practice in Javascript where implicit conversion is
>>>> generally quicker. So in a case where a number can be converted
>>>> implicitly, the cast should be completely skipped and even when a
>>>> number
>>>> needs to be converted to a string, “” + val is more efficient than
>>>> String(val). This is especially true in FlexJS where (I believe) such
>>>> code will result in org.apache.flex.Language.as(val,”String”).
>>> 
>>> I'm not seeing this.  What source code is generating String(val) calls?
>>> Regarding optimization, do we know if GCC will do this ("" + val)
>>> optimization for us?
>>> 
>>> In general, there is a big TODO around type conversions.
>> 
>> Good question. I just checked and there’s both String() and toString()
>> calls in the optimized code. One example:
>> return RA+String(a)+bc+String(this.alpha)}; It’s coming from one of the
>> Graphic classes.
> 
> It would save me time to not have to search through the code for test
> cases if you have them handy.

I already replaced this one. Are you saying you are thinking of doing this replacement in the compiler? I’m not sure it’s really necessary. Implicit string conversions should be fine in Flash as well.

>> 
>> I added a test to toString() performance (toString() is used quite often
>> in the code — much more than String(), and the performance was still
>> worse than implicit conversions:
> 
> Anytime we can avoid making a function call we'd probably save.  The
> question is how important this issue is vs all the other things we have to
> do.

Not terribly, but it is something to keep in mind. I’m changing these as I come across them, and I’m just suggesting others do the same (and don’t create new ones).


Re: [FlexJS] Javascript efficient code patterns

Posted by Alex Harui <ah...@adobe.com>.

On 7/26/16, 11:39 AM, "Harbs" <ha...@gmail.com> wrote:

>
>On Jul 26, 2016, at 8:15 PM, Alex Harui <ah...@adobe.com> wrote:
>
>> 
>> 
>> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
>> 
>>> I noticed a couple of things:
>>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
>>> considered “bad” practice in Javascript where implicit conversion is
>>> generally quicker. So in a case where a number can be converted
>>> implicitly, the cast should be completely skipped and even when a
>>>number
>>> needs to be converted to a string, “” + val is more efficient than
>>> String(val). This is especially true in FlexJS where (I believe) such
>>> code will result in org.apache.flex.Language.as(val,”String”).
>> 
>> I'm not seeing this.  What source code is generating String(val) calls?
>> Regarding optimization, do we know if GCC will do this ("" + val)
>> optimization for us?
>> 
>> In general, there is a big TODO around type conversions.
>
>Good question. I just checked and there’s both String() and toString()
>calls in the optimized code. One example:
>return RA+String(a)+bc+String(this.alpha)}; It’s coming from one of the
>Graphic classes.

It would save me time to not have to search through the code for test
cases if you have them handy.

>
>I added a test to toString() performance (toString() is used quite often
>in the code — much more than String(), and the performance was still
>worse than implicit conversions:

Anytime we can avoid making a function call we'd probably save.  The
question is how important this issue is vs all the other things we have to
do.

-Alex


Re: [FlexJS] Javascript efficient code patterns

Posted by Harbs <ha...@gmail.com>.
On Jul 26, 2016, at 8:15 PM, Alex Harui <ah...@adobe.com> wrote:

> 
> 
> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
> 
>> I noticed a couple of things:
>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
>> considered “bad” practice in Javascript where implicit conversion is
>> generally quicker. So in a case where a number can be converted
>> implicitly, the cast should be completely skipped and even when a number
>> needs to be converted to a string, “” + val is more efficient than
>> String(val). This is especially true in FlexJS where (I believe) such
>> code will result in org.apache.flex.Language.as(val,”String”).
> 
> I'm not seeing this.  What source code is generating String(val) calls?
> Regarding optimization, do we know if GCC will do this ("" + val)
> optimization for us?
> 
> In general, there is a big TODO around type conversions.

Good question. I just checked and there’s both String() and toString() calls in the optimized code. One example:
return RA+String(a)+bc+String(this.alpha)}; It’s coming from one of the Graphic classes.

I added a test to toString() performance (toString() is used quite often in the code — much more than String(), and the performance was still worse than implicit conversions:

function stringCast1(){
	var num = 188;
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var str = "";
	while(++i<len)
	{
		str= String(num);
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};
function stringCast2(){
	var num = 188;
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var str = "";
	while(++i<len)
	{
		str= ""+num;
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};
function stringCast3(){
	var num = 188;
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var str = "";
	while(++i<len)
	{
		str= num.toString();
	}
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};

Firefox:
stringCast1()
4521

stringCast2()
4

stringCast3()
200

Chrome:
stringCast1()
93

stringCast2()
25

stringCast3()
101

I think the  takeaway to to avoid String() and toString() calls. I found 93 places in the minimized code where String() is being used and 548 places where toString() is being used.

> 
>> 
>> 2. String concatenation: I’m not sure how much of an issue this is in
>> current browsers, but using aray.join() to build a string was MUCH faster
>> than building strings using the plus operator. For longer strings the
>> difference was dramatic.
> 
> If I understood this, it would be hard for the compiler to generate the
> right code for all browsers.  You could create a StringBuilder class that
> checks platforms and runs different code at runtime.
> 
> FWIW, Flex UIDUtils uses ByteArray because, IIRC, while array.join() might
> be fast, the array.push() is horribly slow and generates a lot of GC
> activity.


Apparently this is not much of an issue in modern browsers, so I think our efforts are better invested elsewhere.


Re: [FlexJS] Javascript efficient code patterns

Posted by Josh Tynjala <jo...@gmail.com>.
How terrible it is depends on context, I guess. Starling and Feathers try
to maintain 60fps on slightly older mobile hardware. If the GC causes
dropped frames, it's terrible. Switching away from push() (and splice(),
which has two temporary Arrays) made a noticeable difference.

It's worth mentioning that I don't recommend that everyone should avoid
push() at all times. Most people probably aren't calling push() as
frequently as a display list or a scrolling container with a virtual layout
might add and remove children from an Array. If you're filling an array
with many items all at once, or if you might modify the array roughly every
frame while the user is interacting with a component, then push() is
probably adding too much overhead. Otherwise, it's not a big deal. However,
I'd say that a framework should consider avoiding it more often.

- Josh

On Tue, Jul 26, 2016 at 11:27 AM, Harbs <ha...@gmail.com> wrote:

> Interesting. Good to know. Here’s what I got:
>
> function timeJoin(){
>         var start = new Date();
>         var i=-1;
>         var len  = 10000000;
>         var arr = [];
>         while(++i<len)
>         {
>                 arr[i] = "a";
>         }
>         var str = arr.join("");
>         var end = new Date();
>         console.log(end.getTime()-start.getTime());
> };
> function timePush(){
>         var start = new Date();
>         var i=-1;
>         var len  = 10000000;
>         var arr = [];
>         while(++i<len)
>         {
>                 arr.push("a");
>         }
>         var str = arr.join("");
>         var end = new Date();
>         console.log(end.getTime()-start.getTime());
> };
>
> Chrome:
> timeJoin()
> 822
> timePush()
> 979
>
> Firefox:
> timeJoin()
> 184
> timePush()
> 303
>
> Not what I’d call “terrible” (at least for short strings), but indexed
> assignment is definitely faster.
>
> On Jul 26, 2016, at 8:28 PM, Josh Tynjala <jo...@gmail.com> wrote:
>
> > Yeah, push() is terribly bad for performance because of the GC overhead
> > from the ...rest Array. In Starling and Feathers, we always try to avoid
> > push() whenever possible. We usually use bracket syntax instead:
> >
> > array[array.length] = newValue;
> >
> > In loops, it's possible to use a local integer counter instead of
> checking
> > the length property every time.
> >
> > counter = 0; //or counter = array.length, if it isn't empty
> > for(...)
> > {
> >    array[counter] = newValue;
> >    counter++;
> > }
> >
> > - Josh
> >
> >
> > On Tue, Jul 26, 2016 at 10:15 AM, Alex Harui <ah...@adobe.com> wrote:
> >
> >>
> >>
> >> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
> >>
> >>> I noticed a couple of things:
> >>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
> >>> considered “bad” practice in Javascript where implicit conversion is
> >>> generally quicker. So in a case where a number can be converted
> >>> implicitly, the cast should be completely skipped and even when a
> number
> >>> needs to be converted to a string, “” + val is more efficient than
> >>> String(val). This is especially true in FlexJS where (I believe) such
> >>> code will result in org.apache.flex.Language.as(val,”String”).
> >>
> >> I'm not seeing this.  What source code is generating String(val) calls?
> >> Regarding optimization, do we know if GCC will do this ("" + val)
> >> optimization for us?
> >>
> >> In general, there is a big TODO around type conversions.
> >>
> >>>
> >>> 2. String concatenation: I’m not sure how much of an issue this is in
> >>> current browsers, but using aray.join() to build a string was MUCH
> faster
> >>> than building strings using the plus operator. For longer strings the
> >>> difference was dramatic.
> >>
> >> If I understood this, it would be hard for the compiler to generate the
> >> right code for all browsers.  You could create a StringBuilder class
> that
> >> checks platforms and runs different code at runtime.
> >>
> >> FWIW, Flex UIDUtils uses ByteArray because, IIRC, while array.join()
> might
> >> be fast, the array.push() is horribly slow and generates a lot of GC
> >> activity.
> >>
> >> HTH,
> >> -Alex
> >>
> >>
>
>

Re: [FlexJS] Javascript efficient code patterns

Posted by Harbs <ha...@gmail.com>.
Interesting. Good to know. Here’s what I got:

function timeJoin(){
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var arr = [];
	while(++i<len)
	{
		arr[i] = "a";
	}
	var str = arr.join("");
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};
function timePush(){
	var start = new Date();
	var i=-1;
	var len  = 10000000;
	var arr = [];
	while(++i<len)
	{
		arr.push("a");
	}
	var str = arr.join("");
	var end = new Date();
	console.log(end.getTime()-start.getTime());
};

Chrome:
timeJoin()
822
timePush()
979

Firefox:
timeJoin()
184
timePush()
303

Not what I’d call “terrible” (at least for short strings), but indexed assignment is definitely faster.

On Jul 26, 2016, at 8:28 PM, Josh Tynjala <jo...@gmail.com> wrote:

> Yeah, push() is terribly bad for performance because of the GC overhead
> from the ...rest Array. In Starling and Feathers, we always try to avoid
> push() whenever possible. We usually use bracket syntax instead:
> 
> array[array.length] = newValue;
> 
> In loops, it's possible to use a local integer counter instead of checking
> the length property every time.
> 
> counter = 0; //or counter = array.length, if it isn't empty
> for(...)
> {
>    array[counter] = newValue;
>    counter++;
> }
> 
> - Josh
> 
> 
> On Tue, Jul 26, 2016 at 10:15 AM, Alex Harui <ah...@adobe.com> wrote:
> 
>> 
>> 
>> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
>> 
>>> I noticed a couple of things:
>>> 1. There’s lots of String(val) casts in FlexJS code. This practice is
>>> considered “bad” practice in Javascript where implicit conversion is
>>> generally quicker. So in a case where a number can be converted
>>> implicitly, the cast should be completely skipped and even when a number
>>> needs to be converted to a string, “” + val is more efficient than
>>> String(val). This is especially true in FlexJS where (I believe) such
>>> code will result in org.apache.flex.Language.as(val,”String”).
>> 
>> I'm not seeing this.  What source code is generating String(val) calls?
>> Regarding optimization, do we know if GCC will do this ("" + val)
>> optimization for us?
>> 
>> In general, there is a big TODO around type conversions.
>> 
>>> 
>>> 2. String concatenation: I’m not sure how much of an issue this is in
>>> current browsers, but using aray.join() to build a string was MUCH faster
>>> than building strings using the plus operator. For longer strings the
>>> difference was dramatic.
>> 
>> If I understood this, it would be hard for the compiler to generate the
>> right code for all browsers.  You could create a StringBuilder class that
>> checks platforms and runs different code at runtime.
>> 
>> FWIW, Flex UIDUtils uses ByteArray because, IIRC, while array.join() might
>> be fast, the array.push() is horribly slow and generates a lot of GC
>> activity.
>> 
>> HTH,
>> -Alex
>> 
>> 


Re: [FlexJS] Javascript efficient code patterns

Posted by Josh Tynjala <jo...@gmail.com>.
Yeah, push() is terribly bad for performance because of the GC overhead
from the ...rest Array. In Starling and Feathers, we always try to avoid
push() whenever possible. We usually use bracket syntax instead:

array[array.length] = newValue;

In loops, it's possible to use a local integer counter instead of checking
the length property every time.

counter = 0; //or counter = array.length, if it isn't empty
for(...)
{
    array[counter] = newValue;
    counter++;
}

- Josh


On Tue, Jul 26, 2016 at 10:15 AM, Alex Harui <ah...@adobe.com> wrote:

>
>
> On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:
>
> >I noticed a couple of things:
> >1. There’s lots of String(val) casts in FlexJS code. This practice is
> >considered “bad” practice in Javascript where implicit conversion is
> >generally quicker. So in a case where a number can be converted
> >implicitly, the cast should be completely skipped and even when a number
> >needs to be converted to a string, “” + val is more efficient than
> >String(val). This is especially true in FlexJS where (I believe) such
> >code will result in org.apache.flex.Language.as(val,”String”).
>
> I'm not seeing this.  What source code is generating String(val) calls?
> Regarding optimization, do we know if GCC will do this ("" + val)
> optimization for us?
>
> In general, there is a big TODO around type conversions.
>
> >
> >2. String concatenation: I’m not sure how much of an issue this is in
> >current browsers, but using aray.join() to build a string was MUCH faster
> >than building strings using the plus operator. For longer strings the
> >difference was dramatic.
>
> If I understood this, it would be hard for the compiler to generate the
> right code for all browsers.  You could create a StringBuilder class that
> checks platforms and runs different code at runtime.
>
> FWIW, Flex UIDUtils uses ByteArray because, IIRC, while array.join() might
> be fast, the array.push() is horribly slow and generates a lot of GC
> activity.
>
> HTH,
> -Alex
>
>

Re: [FlexJS] Javascript efficient code patterns

Posted by Alex Harui <ah...@adobe.com>.

On 7/26/16, 1:40 AM, "Harbs" <ha...@gmail.com> wrote:

>I noticed a couple of things:
>1. There’s lots of String(val) casts in FlexJS code. This practice is
>considered “bad” practice in Javascript where implicit conversion is
>generally quicker. So in a case where a number can be converted
>implicitly, the cast should be completely skipped and even when a number
>needs to be converted to a string, “” + val is more efficient than
>String(val). This is especially true in FlexJS where (I believe) such
>code will result in org.apache.flex.Language.as(val,”String”).

I'm not seeing this.  What source code is generating String(val) calls?
Regarding optimization, do we know if GCC will do this ("" + val)
optimization for us?

In general, there is a big TODO around type conversions.

>
>2. String concatenation: I’m not sure how much of an issue this is in
>current browsers, but using aray.join() to build a string was MUCH faster
>than building strings using the plus operator. For longer strings the
>difference was dramatic.

If I understood this, it would be hard for the compiler to generate the
right code for all browsers.  You could create a StringBuilder class that
checks platforms and runs different code at runtime.

FWIW, Flex UIDUtils uses ByteArray because, IIRC, while array.join() might
be fast, the array.push() is horribly slow and generates a lot of GC
activity.

HTH,
-Alex