|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionIt is easy to develop your own ASP.NET web application. But making it do some useful things for your users while keeping the design simple and elegant is not so easy. If you are lucky, your web application will be used by more than a handful of users, in that case, performance can become important. For some of the web applications I worked on, performance is vital: the company will lose money if users get frustrated with the slow response. There are many factors that can result in bad performance, the number of users is just one of them. As a developer in a big corporation, you usually don't have a chance to mess with real production servers. However, I think it is very helpful for developers to take a look at the servers that are hosting their applications. Your server spends most of its time waitingProduction servers usually host many applications. One of our web applications was not performing well, I suspect that other applications running on the server were using memory and CPU resources that "should" be devoted to our application. The admin allowed me to look at the server machine, what I found was not what I expected: the server had plenty of unused memory and the CPU usage was pretty low, too. It seems the server was idle most of the time. That means if we design the application differently, we may be able to trade CPU and memory resources for better performance. Application dependenciesIt is typical for web applications to depend on many services running on remote servers. The slow response from those remote servers is likely the real cause of bad performance for a web application. For example, one of our web applications needs to request data from a remote server, a single request alone takes about 3 to 5 seconds. If my application has to make 5 to 7 different requests from remote servers in order to display a web page, then the performance will not be good even if only one user is using the application! My approach for solving the performance problem was to design the application in a way that each page will make as few requests to remote services as possible. Which means the application will not make a remote request to a backend server until the data is really needed and once the data is retrieved, it will be cached within the application so that it doesn't need to request the same data more than once. This approach worked fine for us until ... The management decided to change to a new design that would kill our applicationWhat they want is a more user friendly interface. The first page will be designed in a way that as soon as a user landed on that page, he/she will see a summary of all the important information right away. If more detail is desired, the user can click tabs, links, or buttons on that page to display more data. The problem is, information requested on the first page can only be extracted from data items returned by various remote service calls. There is no single service that can give us such a "summary" of the data. So there is no choice but retrieving all the data items from remote servers before displaying the first page. The performance became so bad that even developers hated to use the application. The SolutionFortunately, our server has extra power to spare and the remote services we need do not depend on each other. After some research, I devised a new way to retrieve data from remote services. Previously, the sequence of steps to get data was as follows:
My idea is, in step 1 while the application is retrieving data item 1, we also let it retrieve other data items in the background asynchronously (and cache the data items once they are received). By the time the app moves to step 2 and step 3, the data items will already be available in cache. Here is the new approach:
Now, let's see the potential difference in performance. With the old approach, suppose it takes 5, 2, 3 seconds to retrieve data items 1, 2, 3, respectively, the total time will be at least 5+2+3 = 10 seconds. With the new approach, since we assume extra server power is available and the remote services are unrelated/independent, the ideal total time will be a little more than the longest of all data requests, which is 5 seconds in this example. So we can reduce the response time by almost 50%! Let me explain the idea again in using plain English (no plain English compiler needed). Let's say you are ordering 3 dishes in a restaurant.
Assuming going back to the kitchen is the most time consuming work, we can save a significant amount of time with the new approach. The ImplementationMy solution is not worth much if I cannot get it to work and other people won't be interested if it is not implemented as a reusable component. Here is what I included with this article. PerormanceEnhancer.dllThis is a DLL that can be reused in any ASP.NET application. It does not have any dependency other than the .NET framework. In order to use this component to call remote services, you need to write at least one wrapper class with a default constructor and implement each remote service call in a public method within the wrapper class. Different types of remote service calls can be implemented in different wrapper classes or in different methods of the same wrapper class. PerformanceEnhancer.dll will be used to call the methods in the wrapper classes in exactly the same way described above, i.e. a mixture of synchronous and asynchronous calls. This component is also responsible for caching. You use PerformanceEnhancer.dll by calling its only public static object[] GetApplicationData
(
string sSessionID,
string sMethodName,
string sClassName,
string sAssemblyPath,
object[] pInput,
bool bUseCache,
bool bClearCache,
int nTimeout,
object[] pMoreRequests
)
Parameter Description
Return Value is an object array of two items:
By checking the Here is what happens under the cover. If the Demo ProgramsThe PETest.dll contains two wrapper classes for demo purposes. The methods in these classes simulate remote service calls that take specified number of seconds to complete. I also included an ASP.NET web application, The TestForm.aspx page makes four requests (i.e. four remote service calls) by calling the two wrapper classes in PETest.dll. It displays the start time, the end time, and the times to make each remote service call. The first request should take 10 seconds, the second 5 seconds, the third 8 seconds, and the fourth 8 seconds. If we do things the old way, then the total time it takes to complete the four requests will be 10+5+8+8 = 31 seconds. However, because we are using PerformanceEnhancer.dll, the total time is rarely more than 11 seconds. This is due to the fact that when the page makes the first request, it also starts to make other requests asynchronously and caches the data retrieved by the asynchronous calls. So the second, third, and fourth requests will find data in cache and return right away. Here is a screen shot of the TestForm.aspx page in the demo app,
As you can see, only the first request takes a little more than 10 seconds, the time to complete other requests can be ignored. If you refresh the page, the results will come back immediately because all the data items have been cached when the page is displayed for the first time. This is the best possible result, of course. Some tips on using PerformanceEnhancer.dllHere is something you need to know about this approach and this component.
Some CommentsSometimes it is possible to solve hard performance problems by combining simple ideas and common sense with careful engineering. Although not rocket science, I think this article provides a good demonstration. Everyone knows about multithreading, but how to use it at the right places in your application is a totally different matter. Unfortunately, creative thinking is not always encouraged in big corporations, Political Correctness (i.e. Teamwork, Process, etc.) is more important than anything else. When I presented my solution, the response I got is "What? You wrote code without a meeting to discuss its design?" I am not even sure they are going to use my solution. Sigh.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||