Friday, March 6, 2009

Designing for Performance

 

bB-2StealthBomber Do you think performance is tuned after the code has been written or is it one of those obvious quality attributes that has to be considered as part of the functional requirements of the system? Considering performance as an afterthought and deciding to tune your software towards the end of the development can only help so much. You really need to consider performance at every stage of software development life cycle. The system's responsiveness needs to be measured and calibrated at every iteration of your product design and development.

One can definitely design systems with that performance as an afterthought notion but I'm not sure whether they would be widely used. Would Google or Amazon have been that popular if they had taken minutes to search for content or books? I bet not. Would Porsche, Ferrari or Stealth Bomber have been possible with the performance-as- afterthought process? I bet not. They were built ground up for performance and speed and they live up to those standards. Why should you believe me? I've driven every single one of them. In my dreams. Every night. Seriously.

So, how does one approach thinking about performance at the design phase? Does one need to employ different design principles for different delivery channels? Though there are specialized patterns which cater to a specific delivery channel like Web, Mobile or desktop based solution but there are some which are overarching and can be applied regardless the delivery channel. IMHO, one needs to consider the following -

  • Caching - Caching of data can dramatically increase the responsiveness of any application though certain considerations need to be made before finalizing the strategy. One could follow W3 of Caching - What, When and Where.
    • What to Cache - It really depends on the application context but anything that would take inordinate amount of time to retrieve and doesn't change that frequently is a good candidate for caching. I also always think about the memory footprint of the cache. I would look at the size of the every object that needs to be cached by enumerating over its properties and computing the actual memory it is going to consume. This helps in the Where part of the puzzle.
    • When to Cache - One really has two options around it - Proactive or Reactive. Proactive Caching is generally employed for datasets(Not the ADO.NET DataSet) that get used in the application in most of the scenarios. It is a technique by which one loads the dataset at the start of the application. Reactive Caching is used when one is not sure when a dataset would get used thus it gets cached it after its first retrieval.
    • Where to Cache - It depends upon how fast the cached dataset needs to be accessed and how big it is? If the cached data doesn't need to be loaded in fractions of milliseconds and is huge then it makes sense to use an Out of Process Cache. But, if the size of the dataset is not that big then one can think about caching the data in the same process. In-process caching gets a little tricky for the web delivery channel when one has a cluster of web servers. As the cache needs to be identical in all the webservers, one has to either develop something in house or use sophisticated products like NCache to replicate the cache state among the clusters.

 

  • Data Structures - Poor selection of data structures can lead to lot of memory wastage and denegenration of performance. Would you really use a LinkedList for storing all your Customers? Would you use an Array for dataset that is always changing? Probably not. I think one needs to decide early the data structures which would get used in the domain model of the product. 

 

  • Algorithms - One not only needs to use the right data structures to store the data but also use the right algorithms to insert/retrieve the data from them. They are tightly coupled and both of them have to be selected in tandem. If you were to sort your dataset, would you use BubbleSort or QuickSort? You wouldn't care if the dataset is too small but using BubbleSort in large datasets could be an extreme wastage of CPU cycles. The selection of the right algorithm plays a huge part in the responsiveness of your application and it makes sense to give a lot of thought to it.

 

  • Asynchronous Behavior - I'm not sure whether asynchronous behavior can  increase the response times of your application but they can tremendously increase the responsivess of your application. In the world of short attention spans and even smaller patience levels, responsiveness means performance. One can use variety of techniques to break the long running transactions and execute them in a step manner while engaging the user. Do you have order processing which runs through a myriad business instructions? Do you have your users see a fascinating marvel called rotating hourglass and twiddle their thumbs after they submit an order? Wouldn't it make sense to break the transaction, put it on a order processing queue and let users know they would be informed when their order is processed? One can easily use some sort of queuing mechanism like MSMQ to decouple the component that submits the order from the service that actually processes the order. In the web scenario, one could use AJAX rather than having the user reload the entire web page again.

 

  • Interface Design - It makes sense to design coarse grained interfaces a.k.a Chunky Interfaces to reduce the chatter among the software layers. It's best to have calls to retrieve and insert data in chunks rathen than invoking multiple method calls to achieve the same logical unit of work. For example, let's say you had an Order class, which had details about the Order, its LineItems and the details about the Customer. Would you have 3 separate calls to create Order, OrderLineItems and Customer or just have one call to create Order, which would execute a transaction to create the Customer if it doesn't exist and then save the Order and its LineItems. It would be prudent to have only one call rather than making 3 independent service calls to achieve this logical unit of work as it reduces the chatter between service layers and help boost performance.

 

  • Data Partitioning - As the slowest moving piece of any application is I/O, it makes sense to give a lot of thought to the database design and how the data would be partitioned in it, if needed.  There are times when certain datasets get used a lot, think of million of hits per day. In those scenarios, it makes sense to partition these datasets with the help of a partition key. The partition key decides how the data would be split physcially. For example, if you are building a social network and you get equal number of english, french and german speaking users then should you divide the users in different databases? It depends upon the context but one should think about the layout of the data persistence. MetaDatabaseIn MS SQL Server 2005, one could even partition a table without having the application ever to know about that the data is distributed in different filegroups. 

 

 

Are there any special design considerations that you know of, which can help boost the performance?

4 comments:

John Caruso said...

Performance is one of those areas where I see many people focus on the wrong things. Some performance-minded developer will decide to avoid .NET DataSets because they're too "heavy." I think however it is very rare that the performance bottleneck or issue ends up being the use of DataSets. The most common performance problems are related to data transporting across processes (i.e., db and web or other network calls).

You already pointed out the need for a course grained API - I would say this is not that important for in process calls, but very, very important for out of process calls, especially where there is high network latency.

I believe whether or not you should design for performance depends on the requirements. Clearly, search engines have "speedy response" as one of the highest priority requirements. But for an internal intranet app used to submit expense reports, performance may not even show up as a requirement (at least not until testing starts and you get feedback if it's too slow).

Anticipating performance - making design decisions or choosing data structures/algorithmns that you think will perform better than something simpler, simply in anticipation that the simpler thing might be a performance issue, is a very very bad thing to do (IMHO).

Take a YAGNI (you aren't gonna need it), do the simplest thing that could possibly work design approach. Then get some real data about your current design by doing some performance testing. Then, and only then, change the design if performance requirements are not met.

I've seen a lot time wasted optimizing code for performance that simply didn't need to be optimized - it was never a real performance issue. Just some developer "designing for performance."

Tarun Kohli said...

John,
I agree with some of what you have said but it looks like that you are carrying the notion that designing for performance takes insane amount of time. It can't be further from truth. Designing for performance doesn't mean that one is working on things that one doesn't need. You mentioned that it performance may not even show up as requirement and it can be done an after thought after getting the feedback, which is 180 degrees to what I was trying to convey - performance doesn't have to be an afterthought, one can easily and quickly design the system by considering some cross cutting concerns.

Furthermore, whether it is a search engine or an intranet application, they both need to work well within the realm of acceptable response times. For example, if one was building an intranet application to display some sort of employee data - how long does it really take to turn Output Caching on in ASP.NET, how long would it take to think of a caching strategy and implement it via the Cache object? Would it really make sense to use Arrays for a dynamic dataset?

We can really make performance-thinking as a second nature and IMHO it doesn't cost much time to design the systems right. I hope you didn't misconstrue the blog post an attempt to evangelize creating gigantic application frameworks to enable a trivial systems. The idea is to think about the data structures, algorithms and other behaviors which help create better systems.

John Caruso said...

Tarun,

No, designing for performance does not necessarily take insane amounts of time. It's not the amount of time to develop, but the amount of time added to future maintenance tasks that concerns me.

My point is that you should start with the simplest and most maintainable design that could work - whether you think it will perform well or not . Then performance test it and only change to a more complex or more costly to maintain design if the tests indicate a problem.

Your example of simply enabling output caching in ASP.NET is not what I'm talking about. That, for the most part is a very simple and relatively transparent change, and any ASP.NET web app is a candidate for such.

I'm talking about for example adding some type of object caching layer to your app. This adds complexity and will very likely increase future maintenance costs. The decision to add such a layer should be made only after performance testing indicates a real problem (IMHO). Don't just assume that you need it to make things perform well. They may already perform perfectly well without it.

I disagree with you that "designing for performance doesn't mean that one is working on things that one doesn't need." Some developers do spend too much time anticipating poor performance, where ultimately there is no real problem. They tune the wrong things.

Yes, I agree that performance doesn't have to be an afterthought, and that one should consider some cross cutting concerns. Specifically one should be very aware of what happens in-process and what happens out-of-process, and use a course grained approach for the latter.

I do feel that if you use a TDD approach, and you have the early feedback and confidence that full unit test suites give you, you can fairly easily refactor your code and add a caching layer, if performance tests indicate a problem.

If you're not using TDD and you don't have those unit tests, then you probably will want to design in caching, up front. As the cost to do it later may be significantly greater.

Tarun Kohli said...

John,
I guess what I'm trying to say is that for most systems performance should really be part of functional specification so that designers/developers can think about the system they are building. Most of the times, one has to pay the price for working on performance issues later on.
One of my architects was part of building this intranet site where no one really talked about the acceptable performance levels to begin with. The system used to stop responding after 20 concurrent users. They followed the right principles but were not cognizant of the expected performance levels.
They were able to rectify the problem but it took lot of hours and sleepless nights to fix the issues.

By no means, I'm advocating to build something, which is not required. I'm just evangelizing using common sense design principles to create better applications. TDD is a great way of building applications, we use it too but it can't be the answer of not thinking ahead about the responsiveness of the application.