Javascript | Performance

Combine Your JavaScript Files. Now.


March 19, 2008 07:55 by joel

Don't argue, just do it. Then read this post to find out why (Hopefully lots of you will already understand why. That's fine. I won't be insulted if you skip this post.).

This topic came up in one of the presentations at Toronto Code Camp this year. Richard Campbell (.NET Rocks) is a great speaker. Not only is he knowledgable, but he's engaging, as well. He's one of those speakers that makes you feel like you'd still be enjoying his presentation, even if you had accidentally stepped into the wrong seminar and had no idea what he was talking about.

The presentation in question was on the topic of "The Scaling Habits of ASP.NET Applications". He provided a ton of useful information, but for the purpose of this post I'm only going to cover one point, which jumped out at me since I spend most of my time on the client-side of things. To be honest, it was the reaction to his comment that jumped out at me the most. The quizzical looks on a lot of the audience, and the number and variety of questions made me realize that a lot of people working in this industry aren't aware of some of the more basic mechanics of browsers and the internet itself. I'm not criticizing, as it is easy to overlook certain things (I'm confident that I make some dumb mistakes, or overlook obvious things pretty often). It just surprised me.

Before you read any farther, download YSlow. You can no longer claim ignorance if questioned. You need this tool (along with Firebug, but you've already got that, right?!?).

Click on the logo in the information bar, and YSlow will analyze the current page, giving a grade from A to F in a variety of performance areas. Expanding an area will give you a bit more detail on the offending files/components and clicking on any of the headings will open a description of what the particular heading refers to. The first area you are marked on, and the one that this post is concerned with, is "Make fewer HTTP requests". Here is Yahoo's description of the best practices for this area. It makes reference to image maps, CSS Sprites, and combining your JavaScript files. Is it really that important, though? What's the difference between one 200k js file and two 100k files?

The root of this problem lies in the fact that the browser can only handle two requests at the same time (this is adjustable from about:config in firefox, and perhaps also in IE, but it won't do you any good, as the limitation is there for a reason and large explosions WILL occur if you tamper with that value). This causes problems because every time you make a request for a particular file (image, css, js, whatever), there is an inherent "round-trip time" (RTT) which is tacked on to the load time of your page. You can't put servers next-door to every one of your users, so a basic analysis using highschool physics reveals the fact that talking to the server takes time. The value of the RTT is out of your hands (unless we start talking about distributed servers and CDN's, but that's way outside the scope of this post, and the budget of most small to medium-sized projects). It's a constant which you need to take into consideration when developing each and every page in your application.

(I debated whether or not to include an equation in this post. I'm not going to, but If you would like to dig deeper into these performance issues, there is a report which Richard made reference to that goes into much greater detail. Although you can just search for it directly, I don't think I'm supposed to link directly to the report. So if you'd like to register first, for their benefit, head here. The report is centered on issues facing Application Delivery Systems. At the outset, however, there is some excellent information on the performance issues facing any developer. The equation on the third page of the report is of particular relevance.)

Often, when trying to optimize the performance of an application, a developer will spend countless hours on the code itself. As client-side programming has become more prevalent, a lot of attention has been focused on the size of JavaScript files. While it's important not to have superfluous code, spending countless hours refining your code is rarely time well spent. If the application does what it is supposed to, optimize other areas first, then as a last resort you can spend some time tweaking the code.

To illustrate what I am saying, let's consider a small example. Assume that the total size of all files associated with a certain page is 350.5K. Internet connections are getting faster, generally, so I'll just use my connection as an example. According to speedtest.net, my download speed is 6846kb/s. This means that bandwidth accounts for approximately 51ms of the total page load time in this example. Yet the page took 3.627s to load (initial load, no caching). There are a lot of other factors affecting the load time, all of which are included in the equation which I mentioned previously. RTT, however, is the one that this post is concerned with, so let's see how it affects the total time it takes our page to load. When I ping the server, I get an RTT of 102ms. So, with a total of 27 HTTP requests, at 102ms each, taking into account that we can do 2 at a time, we get a total of 1.377s. This means that 38% of our load time is attributable directly and exclusively to the length of time it takes something to get from the client to the server and back again.

Let's go back for a second to the ways that most developers initially try to optimize their page-load time. Optimizing your script files is going to make a bit of a difference. But there's only so much you can do there. You might spend two days optimizing your code and find that you can cut a few Kb off the filesize and 30 to 40 milliseconds off the time it takes the browser to parse the page. If you've done a good job writing your code, that much of a savings might not even be realistic. We'll use it as an example anyway. You've just spent two days and managed to pull off a 1% savings on initial load time. "Not too bad" you say as you pat yourself on the back.

Now let's pretend that you left your computer on and your 10 year-old, just learning the intricacies of notepad and figuring he'd give you a hand, cut and pasted all of your 8 JavaScript files into the same file. When you discover what has happened, you panic, but decide to load the page anyway, just to see what happens. To your surprise, the page still works, and it loads in 3.27s. This means that in 15 action-packed minutes, your 10 year-old managed to cut 357ms from the load time: somewhere in the vicinity of 10 times better than you were able to do in two days of work.

That was obviously a very simplified example. If you check out the resource that I mentioned (mainly the various components of the equation), you'll realize that there are many more factors involved in the time it takes to load the page. The load times I mentioned, and the savings I mentioned, however, are very real. You won't have to look very far to find web pages that pull in eight or more JavaScript files. Cutting their size, using a utility such as Yahoo Compressor, and gzipping them are all important optimizations that you should be doing already. When you combine that with a reduction in the number of requests you are making you will have achieved the biggest performance boost that your page can get, without adjusting a single line of code. When you begin to dig deeper than that, the solutions will continue to get more difficult, more expensive, and will provide you with smaller performance enhancements.

What I hope you take away from this post: Do the cheap and easy optimizations first, and the expensive and time consuming options may not even be necessary.



Javascript

Instantiating Server-Side Objects From the Client Using ASP.NET AJAX


March 3, 2008 08:40 by joel

The topic for this post grew out of a number of conversations I've had in recent weeks on subjects relating to WebMethods. For the purposes of this post, I'm going to focus on the the more specific subject of instantiating server-side objects from the client. Although it is a relatively simple thing to accomplish, I think it provides something very tangible and useful to use a starting point for understanding the different ways that the ASP.NET AJAX framework simplifies the exchange of data between the client and the server. 

To begin, I'm going to fire up Visual Studio and create a new ASP.NET 3.5 website (note, however, that everything I am going to do in this post works pretty much the same in ASP.NET 2.0 with the AJAX framework installed). The first step is to define the class which we want to instantiate from the client. Let's use a person as an example. We'll give the class Name, Age, and Weight properties :

Class Screen Shot

Note that I've used a shortcut provided by C#3.0. If your getter and setter don't require any additional logic, you can simply declare your properties as I did for this class. It is just a shortcut provided by the compiler, and the underlying CIL will be exactly the same as if you had defined the properties in the old way, using private variables and getter/setter methods.

The next step is to create a WebService. To keep things simple, the WebMethod will take a Person object as an argument and will return a string.

WebService

The cool thing about this is that since the method takes a Person as an argument, once you register the WebService with the ScriptManager, the client will automatically have a definition of Person. In fact, with VS2008, you'll even get intellisense for it. So for this example, instantiating a Person on the client is as simple as creating a new object, like so:   

the js

All I'm going to do to demonstrate this is call the HelloWorld method, passing it the Person object which I created. In the callback function, I'll simply pass the returned string to an alert: 

the demo

If you try this out, you'll see that as you might expect, you get an alert which says "Hello Bob". So what exactly is going on?

One of the goals of the ASP.NET AJAX Framework is to bridge the gap between the client and the server. In practice, this means that a developer can use a combination of server-side and client-side logic to solve a problem. It does this in a number of ways, including automatically serializing and de-serializing the data on both the client and the server. I won't get into great detail here, as I don't think it's profitable for this discussion. If, however, you'd like a more thorough explanation of what is going on behind the scenes, take a look here. To help you understand what happened in this specific example, we'll take a quick peek behind the scenes using firebug:

firebug

What you are looking at is an automatically generated script file. Most of the file is made up of a JavaScript proxy object which does the work of enabling access to the WebService. The last few lines that I've circled create a definition of a Person object on the client, which is then associated with the server-side Person class. It does this by generating a typed constructor, which basically just means that each Person object that you create will have a __type property, containing the name of the class. Because of that definition, you can both pass a Person to the server as an argument to a WebMethod, or return a Person back to the client, and it will be recognized in both instances. Another cool thing that you'll discover if you dig a bit deeper is that you can also pass collections back and forth. For example, an array or a generic list of Person objects will also be recognized on both the client and server. 

Before I finish, I'll mention one more scenario. If you happen to have a class on the server that you aren't necessarily using in any of your WebMethods, but you want to be able to instantiate and use it on the client, you can add the GenerateScriptType attribute to your WebService. Assuming that I've created another class, called Pet, here is the new WebService:

new webservice

With that attribute set, it is now possible to create a new Pet object in JavaScript, on the client. One thing to remember, as you begin passing objects back and forth between the client and server, is that JavaScript is a dynamic language. The immediately obvious consequence of that fact is that you can add new properties to a JavaScript object at any time. So although the server-side Person may only have Name, Age, and Weight, it is technically possible to add new properties to your client-side Person at any time during program execution. The consequence of this could be lost data. If, for example, you were to add a ShoeSize property to your client object, and then pass the Person as an argument to a WebMethod, there won't be any error. The Person on the server will simply be populated with the Name, Age, and Weight, but the ShoeSize data will be thrown away.

And finally I'll mention something that came up during a conversation I had at Toronto Code Camp on this topic. During the conversation, we were wondering whether or not the generated constructor contains definitions for the properties of the server-side object. I looked into it yesterday and discovered that it does not. In practice, this doesn't actually matter, however. Being a dynamic language, the following two snippets of code in JavaScript produce exactly the same result:

Example 1

function Person() {
this.Age = null;
this.Name = null;
}
var Bob = new Person();
Bob.Name = "Bob";
Bob.Age = 35;

Example 2

function Person() {}
var Bob = new Person();
Bob.Name = "Bob";
Bob.Age = 35;

Basically the only real downside of the constructor not getting pre-populated with the correct properties is that you don't get intellisense for your client-side objects until you begin to assign them properties. Since JavaScript intellisense is a very new thing in Visual Studio, anyway, probably this is a problem I can overlook for the moment. :)