Return to HomePage



Code Review: Web Services 1.1 Performance

Source: http://msdn.microsoft.com/library/en-us/dnpag/html/ScaleNetChapt13.asp
J.D. Meier, Srinath Vasireddy, Ashish Babbar, Rico Mariani, and Alex Mackman


Web Services

Use the review questions in this section to assess the efficiency of your Web services as well as the client code which calls your Web services.
Web Methods
Review your Web method implementation by using the following questions:
* Do you use primitive types as parameters for Web methods?
Regardless of the encoding style you use, you should prefer simple primitive types such as int, double, and string as parameters for Web services. These types require less serialization effort and are easily validated.
* Do you validate the input with a schema before processing it?
We strongly recommend having a schema and using it to assist in the design and debug phases even if strong validation is inappropriate for production. From a security standpoint, you should validate input. Finding and rejecting invalid input early can also help avoid redundant processing time and CPU utilization. However, validating XML input using schemas introduces additional processing overhead, and you need to balance the benefits of validation against this additional cost for your particular application to determine whether validation is appropriate.
If you do use validation, make sure you optimize schema validation performance, for example, by compiling and caching the schema. You can validate incoming messages in a separate HttpModule, SoapExtension or within the Web method itself. For more information, see "Validating XML" in Chapter 9, "Improving XML Performance."
* Do you perform I/O operations in your Web service?
If your code performs I/O bound operations such as file access, consider using an asynchronous Web method. An asynchronous implementation helps in cases where you want to free up the worker thread instead of waiting for the results to return from a potentially long-running task.
You should not implement asynchronous Web methods when making a long - running database call because you end up using delegates that require a worker thread for asynchronous processing. This degrades performance rather than increasing it.
* Does the client expect data back from the Web service?
If your client does not expect data from the Web service, check if your code uses the OneWay attribute on the Web method so that the client does not wait on any results.

public class BatchOperations : WebService {
[SoapDocumentMethod(OneWay=true),
WebMethod(Description="Starts long running operation 1 .")]
public void ProcessLongRunningOp1(){
// Start processing
}
}


Web Service Clients
Use the following review questions help review your Web service consumer code:
* Have you considered calling Web services asynchronously?
* Do you make long-running calls to Web services?
* Do you use XMLIgnore to reduce the amount of data sent over wire?
* Are client timeouts greater then your Web service timeout?
* Do you abort connections when ASP.NET pages timeout?
* Do you use PreAuthentication with Basic authentication?
* Do you use UnsafeAuthenticatedConnectionString with Windows authentication?
* Have you configured your connections?
* Have you tuned the thread pool on the server and client?

Have You Considered Calling Web Services Asynchronously?
You can improve performance on the client by invoking Web services asynchronously. The proxy generated by Visual Studio® .NET automatically provides two extra methods for asynchronous invocation of the Web service. For example, if you have a function named MyProcess, Visual Studio .NET automatically generates two additional functions named BeginMyProcess and EndMyProcess.
For Windows Forms-based applications, you should use asynchronous invocation to keep the user interface (UI) responsive to user actions. For server applications, you should invoke Web services asynchronously when you can free up the worker thread to do some useful work.
Do You Make Long-Running Calls to Web Services?
If your Web service calls are long-running, you can free up the worker thread for useful work by invoking the Web services asynchronously.
For more information see “How To: Submit and Poll for Long-Running Tasks” in the “How To” section of this guide.
Do You Use XMLIgnore To Reduce the Amount of Data Sent Over the Wire?
Use the XMLIgnore attribute to avoid sending unnecessary data over the wire. By default, XML serialization serializes all the public properties and fields of a class. If your class includes derived data or codes that you do not want to return to the client, you can mark members with the XmlIgnore attribute.
As a design consideration, you should consider passing custom classes to and from Web services. This is an efficient approach. The class does not need to correspond one - to - one with internal structures used by the clients or the Web service.
Are Client Timeouts Greater Than Your Web Service Timeout?
Ensure that the client timeouts calling the Web service are greater than the Web service timeout. Consider the following guidelines:
* When calling a Web service synchronously, ensure that the proxy timeout is set appropriately.
* Set the executionTimeout attribute for the HttpRunTime element to a higher value than the proxy timeout for the Web service.

Do You Abort Connections When ASP.NET Pages Timeout?
If you have an ASP.NET page that calls a Web service, it is possible for the page request to time out before the page receives a response back from the Web service. In this event, the connection to the Web service does not get aborted and the Web service request continues to execute, eventually returning despite the client page timing out.
To address this issue, tune your time-outs and modify the automatically generated proxy code to abort the request if the ASP.NET page times out.
For more information, about tuning time-outs for Web services, see "Web Services Tuning" in Chapter 17, "Tuning .NET Application Performance." For more information about how to abort Web service connections for timed-out Web pages, see "Timeouts" in Chapter 10, "Improving Web Services Performance."
Do You Use PreAuthenticate with Basic Authentication?
To save rounds trips between the client and server, use the PreAuthenticate property of the proxy when using basic authentication. Pre-authentication applies only after the Web service successfully authenticates the first time. Pre-authentication has no impact on the first Web request. For more information, see "Connections" in Chapter 10, "Improving Web Services Performance."
Do You Use UnsafeAuthenticatedConnectionString with Windows Authentication?
If your ASP.NET application calls a Web service that uses Windows Integrated Authentication, consider enabling UnsafeAuthenticatedConnectionSharing. By default, when you connect using Windows authentication, connections are opened and closed per request. Enabling UnsafeAuthenticatedSharing keeps connections open so they can be reused. If you enable UnsafeAuthenticatedSharing, the same connection is reused for multiple requests from different users. This may not be desirable if you need to flow the identity of the user when making the calls.
For more information, see "Connections" in Chapter 10, "Improving Web Services Performance."
Have You Configured Your Connections?
If you are calling multiple Web services, you can prioritize and allocate connections using the ConnectionManagement element in Machine.config.
If you call a remote Web service from an ASP.NET application, ensure that you have configured the maxconnection setting in Machine.config. You can consider increasing this to twelve times the number of CPUs if you have processor utilization below the threshold limits.
Have You Tuned the Thread Pool on the Server and Client?
Before deploying your application, ensure that the thread pool has been tuned for your client (where appropriate) and your Web service. Appropriate tuning of the thread pool can improve performance drastically. The important attributes are: maxWorkerThreads, maxIOThreads, minFreeThreads, and minLocalRequestFreeThreads.
Tuning the thread pool affects the number of requests which can concurrently be processed by the server. This drives other decisions, such as the size of the connection pool to the database, and the number of concurrent connections to a remote Web service (defined by maxconnection in Machine.config).
For more information, see "Threading" in Chapter 10, "Improving Web Services Performance."
More Information
For more information about the issues raised in this section, see Chapter 10, "Improving Web Services Performance."
Remoting
Use the following review questions to analyze your use and choice of .NET remoting:
* Do you use MarshalByRef and MarshalByValue appropriately?
* Do you use the HttpChannel?
* Do you need to transfer large amounts of data over the HttpChannel?
* Which formatter do you use to serialize data?
* Do you send all the data across the wire?
* Do you serialize ADO.NET objects using the BinaryFormatter?
* Have you considered calling remote components asynchronously?


Do You Use MarshalByRef and MarshalByValue Appropriately?
Identify places in your code where you are using MarshalByRef and MarshalByValue. Ensure that you are using the appropriate one.
Use MarshalByRef in the following situations:
* The state of the object should stay in the host application domain.
* The size of the objects is prohibitively large.

Use MarshalByValue in the following situations:
* You do not need to update the data on the server.
* You need to pass the complete state of the object.

Do You Use the HttpChannel?
If you use the HttpChannel for .NET remoting, you should prefer IIS as the host for the remote component because the component is loaded in the ASP.NET worker process. The ASP.NET worker process loads the server garbage collector, which is more efficient for garbage collection on multiprocessor machines. If you use a custom host, such as a Windows service, you can use only the workstation garbage collector. The HttpChannel also enables you to load balance components hosted in IIS.
Do You Need to Transfer Large Amounts of Data over the HttpChannel?
Consider reducing the amount of data being serialized. Mark any member that does not need to be serialized with the NonSerialized attribute to avoid serialization. However, if you still pass large amounts of data, consider using HTTP 1.1 compression by hosting the objects in IIS. You need to develop a custom proxy for compressing and decompressing the data at the client side. This can add an extra layer of complexity as well as development time for your application.
Which Formatter Do You Use To Serialize Data?
If you need to use the SoapFormatter, consider using Web services instead of remoting. SOAP-based communication in Web services outperforms remoting in most scenarios.
Prefer the BinaryFormatter for optimum performance when using .NET remoting. The BinaryFormatter creates a compact binary wire representation for the data passed across the boundary. This reduces the amount of data getting passed over the network.
Do You Send All The Data Across The Wire?
Sending an entire data structure across the wire can be expensive. Evaluate the data structures you are sending across the wire to determine whether you need to pass all the data associated with that data structure. The internal representation of the data need not be same as the one transmitted across remoting boundaries.
Mark members that do not need to be serialized with the NonSerialized attribute.
Do You Serialize ADO.NET Objects using BinaryFormatter?
Serializing ADO.NET objects using BinaryFormatter still causes them to be serialized as XML. As a result, the size of data passed over the wire is high for ADO.NET objects. In most cases, you can optimize the serialization of ADO.NET objects by implementing your own serialization for these objects.
More Information
For more information, see the following resources:
* "How To: Improve Serialization Performance" in the “How To” section of this guide.
* Knowledge Base article 829740, "Improving DataSet Serialization and Remoting Performance," at http://support.microsoft.com/default.aspx?scid=kb;en-us;829740.
* "Binary Serialization of ADO.NET Objects" in MSDN Magazine at http://msdn.microsoft.com/msdnmag/issues/02/12/CuttingEdge/default.aspx.
* If you serialize using DataSet, see "Do You Use DataSets?” in the "DataSets" section later in this chapter.

Have You Considered Asynchronous Calls to the Remote Component?
For server applications, you should consider asynchronous calls when you can free up the worker thread to do some other useful work. The worker thread can be completely freed to handle more incoming requests, or partially freed to do some useful work before blocking for the results.
More Information
For more information about the issues raised in this section, see Chapter 11, "Improving Remoting Performance."
Data Access
Use the following questions in this section to review the efficiency of your application's data access:
* Do you use connections efficiently?
* Do you use commands efficiently?
* Do you use stored procedures efficiently?
* Do you use Transact-SQL?
* Do you use Parameters?
* Do you use DataReaders?
* Do you use DataSets?
* Do you use Transactions?
* Do you use Binary Large Objects (BLOBS)?
* Do you page through data?

Do You Use Connections Efficiently?
Use the following review questions to review your code's use of database connections:
* Do you close your connections properly?
Keeping too many open connections is a common pitfall. Ensure you close your connections properly to reduce resource pressure. Identify areas in your code where you are using connections, and ensure the following guidelines are followed:
* Open and close the connection within the method.
* Explicitly close connections using a finally or using block.
* When using DataReaders, specify CommandBehavior.CloseConnection.
* If using Fill or Update with a DataSet, do not explicitly open the connection. The Fill and Update methods automatically open and close the connection.

* Do you pool your database connections?
Creating database connections is expensive. You can reduce the creation overhead by pooling your database connections.
You can pool connections by connecting to a database as a single identity rather than flowing the identity of original caller to the database. Flowing the caller's identity results in a separate connection pool for each user. Changing the connection string even by adding an empty space creates a separate pool for that connection string. If you are pooling your database connections, make certain that you call Close or Dispose on the connection as soon as you are done with the connection. This ensures that it is promptly returned to the pool.
* Is the pool size set correctly?
It is important to optimize the maximum and minimum levels of the pool size to maximize the throughput for your application. If you set the maximum levels to values that are too high, you may end up creating deadlocks and heavy resource utilization on the database. If you use values that are too low, you run the risk of under utilizing the database and queuing up the requests.
Determine appropriate maximum and minimum values for the pool during performance testing and performance tuning.
* What data provider do you use?
Make sure that your code uses the correct data provider. Each database-specific provider is optimized for a particular database:
* Use System.Data.SqlClient for SQL Server 7.0 and later.
* Use System.Data.OleDb for SQL Server 6.5 or OLE DB providers.
* Use System.Data.ODBC for ODBC data sources.
* Use System.Data.Oracle.Client for Oracle.
* Use SQLXML managed classes for XML data and SQL Server 2000.

* Do you check the State property of OleDbConnection?
Using the State property causes an additional round trip to the database. If you need to check the status of the connection, consider handling the StateChange event.

More Information
For more information about the questions and issues raised in this section, see "Connections" in Chapter 12, "Improving ADO.NET Performance."
Do You Use Commands Efficiently?
Use the following review questions to help review how efficiently your code uses database commands:
* Do you execute queries that do not return data?
If you do not return values from your stored procedure, use ExecuteNonQuery for optimum performance.
* Do you execute queries that only return a single value?
Identify queries that return only a single value. Consider changing the query to use return values and use Command.ExecuteNonQuery, or if you do not have control over the query, use Command.ExecuteScaler, which returns the value of the first column of the first row.
* Do you access very wide rows or rows with BLOBs?
If you are accessing very wide rows or rows with BLOB data, use CommandBehavior.SequentialAccess in conjunction with GetBytes to access BLOB in chunks.
* Do you use CommandBuilder at runtime?
CommandBuilder objects are useful for design time, prototyping, and code generation. However, you should avoid using them in production applications because the processing required to generate commands can affect performance. Ensure you are not using the CommandBuilder objects at run time.

More Information
For more information about the questions and issues raised in this section, see "Commands" in Chapter 12, "Improving ADO.NET Performance."
Do You Use Stored Procedures?
Use the following review questions to review your code's use of stored procedures:
* Have you analyzed the stored procedure query plan?
During your application's development stage, you should analyze your stored procedure query plan. Recompilation is not necessarily a bad thing; the optimizer recompiles when initial plan is not optimal for other calls. By monitoring and reducing frequent recompilation, you could avoid performance hits. You can monitor recompiling stored procedures by creating a trace in SQL Profiler and track for the SP:Recompile event. Identify the cause of recompilation and take corrective actions. For more information, see “Execution Plan Recompiles” in Chapter 14, “Improving SQL Server Performance.”
* Do you have multiple statements within the stored procedure?
Use SET NOCOUNT ON when you have multiple statements within your stored procedures. This prevents SQL Server from sending the DONEINPROC message for each statement in the stored procedure and reduces the processing SQL Server performs, as well as the size of the response sent across the network.
* Do you return a resultset for small amounts of data?
You should use output parameters and ExecuteNonQuery to return small amounts of data instead of returning a result set that contains a single row. This avoids the performance overhead associated with creating the result set on the server. If you need to return several output parameters, you can select them into variables and then emit a single row by selecting with all the variables so there’s one resultset for all.
* Do you use CommandType.Text with OleDbCommand?
If you use the OleDbCommand, use CommandType.Text. If you use CommandType.StoredProcedure, ODBC call syntax is generated by the provider anyway. By using explicit call syntax, you reduce the work of the provider.

More Information
For more information about the questions and issues raised in this section, see "Stored Procedures" in Chapter 12, "Improving ADO.NET Performance."
Do You Use Transact SQL?
If you use T-SQL, review the following questions:
* Do you restrict the amount of data selected?
Returning large amounts of data increases query time and the time it takes to transfer the data across the network. Similarly updating large amounts of data increases the load on the database server. Avoid using SELECT * in your queries and check that you restrict the amount of data that you select in your queries, for example, by using an appropriate WHERE clause.
* Do you use Select Top in rows?
Using Top in your SELECT statements enables you to limit the number of rows that can be returned by the select command. If you implement client-side paging, it makes sense to make use this feature. The query processing is aborted when the specified number of rows have been retrieved.
For more information about paging data, see "How To: Page Records in .NET Applications" in the “How To” section of this guide.
* Do you select only the columns you need?
Select only columns you need instead of using SELECT * queries. This reduces the network traffic in addition to reducing the processing on the database server.
Reducing your columns to the minimum also makes it easier for SQL Server to use an index to cover your query. If all the columns you need are in a usable index that is smaller than the main table, less I/O is required because the index contains the full result. Indexes are often created exactly for this reason, or columns are added to existing indexes not because of the sorting needs but to make the index better at “covering” the necessary queries. Creation of “covering” indexes is vital because if the index does not cover the query, the main table needs to be access (a so - called bookmark lookup from the index). From a performance perspective, these are equivalent to using joins.
* Do you batch multiple queries to avoid round trips?
Batching is the process of sending several SQL statements in one trip to the server. Batching can increase performance by reducing round trips to the database. Where possible, batch multiple SQL statements together and use the DataReader.NextResult method to improve performance. Another alternative is to batch multiple SQL statements within a stored procedure.

Do You Use Parameters?
Use the following review questions to review your code's use of parameters:
* Do you use parameters for all your stored procedures and SQL statements?
Using parameters when calling SQL statements as well as stored procedures can increase performance. Identify areas in your code where you call SQL statements or stored procedures, and ensure that you are explicitly creating parameters and supplying the parameter type, size, precision, and scale.
* Do you explicitly specify the parameter types?
Specifying the parameter types prevents unnecessary type conversions that are otherwise performed by the data provider. Use the enumeration type that is relevant for the connection used by you; for example, SqlDbType or OledbType.
* Do you cache the parameters for a frequently called stored procedure?
Consider caching the stored procedure parameters if you invoke stored procedures frequently to improve performance. If ASP.NET pages calls stored procedures, you can use cache APIs. If your data access code is factored into a separate component, caching helps only if your components are stateful. A good approach is to cache parameter arrays in a Hashtable. Each parameter array contains the parameters that are required by a particular stored procedure used by a particular connection.

More Information
For more information about the questions and issues raised in this section, see "Parameters" in Chapter 12, "Improving ADO.NET Performance."
Do you use DataReaders?
If you use DataReaders, review the following questions:
* Do you close your DataReaders?
Scan your code to ensure you are closing your DataReaders as soon as you are finished with them. You should call Close or Dispose in a finally block. If you pass a DateReader back from a method, use CommandBahavior.CloseConnection to ensure the connection gets closed when the reader is closed.
* Do you use index to read from a DataReader?
All output from a DataReader should be read using an index (for example, rdr.GetString(0)) which is faster, but for readability and maintainability, you might prefer to use the string names of the columns. If you are accessing the same columns multiple times (for example, when you retrieve a number of rows), you should use local variables that store the index number of the columns. You can use rdr.GetOrdinal() to retrieve the ordinal position of a column.
For more information, see "Use GetOrdinal when Using an Index-Based Lookup" in Chapter 12, "Improving ADO.NET Performance."

Do You Use DataSets?
Use the following review questions to review your code's use of DataSets:
* Do you serialize DataSets?
Inefficient serializing of DataSets is a major performance issue for remote calls. You should avoid sending DataSets (especially when using .NET remoting) and consider alternative means of sending data over the wire, such as arrays or simple collections, where possible.
If you serialize DataSets, make sure you adhere to the following guidelines:
* Only return relevant data in the DataSet.
* Consider using alias column names to shorter actual column names. This helps reduce the size of the DataSet.
* Avoid multiple versions of the data. Call AcceptChanges before serializing a DataSet.
* When serializing a DataSet over a Remoting channel, use the DataSetSurrogate class.

For more information, see "How To: Improve Serialization Performance" in the “How To” section of this guide, and Knowledge Base article 829740, "Improving DataSet Serialization and Remoting Performance," at http://support.microsoft.com/default.aspx?scid=kb;en-us;829740.
* Do you search data which has a primary key column?
If you need to search a DataSet using a primary key, create the primary key on the DataTable. This creates an index that the Rows.Find method can use to quickly find the required records. Avoid using DataTable.Select, which does not use indices.
* Do you search data which does not have a primary key?
If you need to repetitively search by nonprimary key data, create a DataView with a sort order. This creates an index that can be used to improve search efficiency. This is best suited to repetitive searches as there is some cost to creating the index.
* Do you use DataSets for XML data?
If you do not pass the schema for the XML data, the DataSet tries to infer the schema at run time. Pass XmlReadMode.IgnoreSchema to the ReadXml method to ensure that schema is not inferred.

More Information
For more information about the questions and issues raised in this section, see "Connections" in Chapter 12, "Improving ADO.NET Performance."
Do You Use Transactions?
Use the following review questions to review your code's use of transactions:
* What isolation level do you use?
Different isolation levels have different costs. Applications may have to operate at different transaction isolation levels, depending on their business needs. You need to choose the isolation level that is appropriate for the scenario. For example, scenarios that require a high degree of data integrity need a higher isolation level.
* Do you have long-running transactions?
Having a long-running transaction with high isolation levels prevents other users from reading the data. Instead of locking resources for the duration of the transaction, consider accommodating various states within your schema (for example, ticket status PENDING, instead of locking the row). Another option is to use compensating transactions.
* Did you turn off automatic transaction enlistment if it’s not needed?
If you use the.NET Framework Data Provider for SQL Server, you can turn off automatic transaction enlistment by setting Enlist to false in the connection string, as shown in the following code, when you are not dealing with an existing distribution transaction:

SqlConnection LondonSqlConnection = new SqlConnection( "Server=London;Integrated Security=true;Enlist=false;");


More Information
For more information about the questions and issues raised in this section, see "Transactions" in Chapter 12, "Improving ADO.NET Performance."
Do You Use Binary Large Objects (BLOBS)?
Use the following review questions to review your code's use of BLOB data:
* Do you store BLOBs in the database?
Reading and writing BLOBs to and from a database is an expensive operation, not only from a database perspective, but also from a code perspective. This is because there is also a memory impact associated with accessing BLOB data. If you store files such as images or documents that are frequently accessed by a Web server, consider storing the files on the Web server’s file system and maintaining a list of all the objects in the database. This can increase performance by avoiding frequent moving of BLOBs from the database to the Web server.

Note: This approach adds a maintenance overhead of having to update the links if the file path changes.

If you have a large store of images that is too large for a Web server, storing it in the SQL database as BLOBs is the right choice.
* Do you use a DataReader to read BLOBs?
If you access BLOB data, check that you use CommandBehavior.SequentialAccess in conjunction with the GetBytes, GetChars, or GetString methods to read BLOB in chunks.
* Do you read or write BLOBs to SQL Server database?
Ensure that you use READTEXT and UPDATETEXT to read and write large BLOBs to a SQL Server database. Use READTEXT to read text, ntext, varchar, varbinary, or image values. This enables you to read the data in chunks to improve performance. Use UPDATETEXT to write data in chunks.
However, if you "BLOB" an item that is relatively small, you can consider reading it in a statement or operation rather than in chunks. This depends on your network bandwidth and workload.
* Do you read or write BLOBs to an Oracle database?
Ensure that you use the System.Data.OracleClient.OracleLob class to read and write BLOBs to an Oracle database. The Read and Write methods provide the flexibility of reading and writing the data in chunks.

More Information
For more information about the questions and issues raised in this section, see "Binary Large Objects (BLOBS)" in Chapter 12, "Improving ADO.NET Performance."
Do You Page Through Data?
Use the following review questions to review your code's use of paging records:
* Do you page data based on user query (such as results of a search query)?
If you need to page through a large amount of data based on user queries, consider using SELECT TOP along with the table data type in your stored procedures. For more information, see “How To: Page Records in .NET Applications” in the "How To" section of this guide.
* Do you page through data which is mostly static over a period of time?
If you need to page through large amounts of data that is same for all users and is mostly static, consider using SELECT TOP along with the global temptable in your stored procedures. If you take this approach, ensure you have a policy in place to manage factors, such as refreshing the temp table with current data. For more information refer to “How To: Page Records in .NET Applications”.

More Information
For more information about the issues raised in this section, see Chapter 12, "Improving ADO.NET Performance."
Summary
Performance and scalability code reviews are similar to regular code reviews or inspections, except that the focus is on the identification of coding flaws that can lead to reduced performance and scalability.
This chapter has shown how to review managed code for top performance and scalability issues. It has also shown you how to identify other more subtle flaws that can lead to performance and scalability issues.
Performance and scalability code reviews are not a panacea. However, they can be very effective and should be a regular milestone in the development life cycle.
Additional Resource
For more information, see the following resources:
* Chapter 4 "Architecture and Design Review of a .NET Application for Performance and Scalability."
* Chapter 6, "Improving ASP.NET Performance."
* Chapter 7, "Improving Interop Performance."
* Chapter 8, "Improving Enterprise Services Performance."
* Chapter 9, "Improving XML Performance."
* Chapter 10, "Improving Web Services Performance."
* Chapter 11, "Improving Remoting Performance."
* Chapter 12, "Improving ADO.NET Performance."
For printable checklists, see the following checklists in the "Checklists” section of this guide:
* "Checklist: ASP.NET Performance."
* "Checklist: Managed Code Performance."
* "Checklist: Enterprise Services Performance."
* "Checklist: Interop Performance."
* "Checklist: Remoting Performance."
* "Checklist: Web Services Performance."
* "Checklist: XML Performance."
For further reading, see the following resource:
* For more information about designing for performance, see "Performance" on MSDN at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsent7/html/vxconPerformance.asp?frame=true.




Return to HomePage
Microsoft Communities