|
Web Site Optimization Techniques |
|
See Also: |
Two-phase content generation offers high-performance pages, while keeping some page content dynamic.
Two-phase content generation is an optimization technique where a web page contains code that is executed in two phases. The first phase is executed by APGen, and the second phase is executed by ASP or another dynamic web page product.
Figure 1 compares a standard ASP page to a two-phase APG script. In the two-phase model, the first phase is when an APG script is executed that generates an ASP file. In the second phase, the ASP file is executed and an HTTP stream is returned to a browser.
Figure 1: Two Phase Content Generation vs. Standard ASP
First phase content generation occurs during a programmable event, such as when an administrator runs a "build" web page, a SQL trigger fires, or a scheduled rebuild occurs. To invoke the first phase, use the APGen COM object to execute an APG script. It is up to the web site architect to determine what sorts of events will cause execution of the first phase.
Two-phase content generation is a compromise between static pages and dynamic pages: Two-phase offers superior performance to dynamic pages, but does not perform as well as static pages; it offers some dynamic content, but the page becomes faster as more dynamic content is eliminated from the second phase. The more code that can be run in the first phase, the faster a page will be. User specific functionality (such as shopping cart contents, serving ads, or determining whether the user is logged on) must be performed in the second phase, and shared, less volatile content (such as product information) can be rendered in the first phase.
Two-phase content generation typically performs 3 to 100 times better than dynamic pages. Results depend upon how much code can be moved into the first phase. Two-phase content generation generally performs much better than output caching or data caching. Caching always involves storage and lookup, and two-phase skips these steps. Two-phase content generation also avoids cache validity checks, which slows caching and makes caching more difficult to implement.
Two-phase content generation requires some additional development work compared to standard ASP only pages. The developer must decide which code is run in the first phase, and which code is run in the second phase (this is discussed below). The additional complexity is comparable to writing ASPs with server-side code and client-side code. With client-side code, web page code is moved from the server to the client to improve the user interface, reduce web server round-trips, and reduce load on the web server. Similarly, web page code can be moved from the web server to the first phase to reduce load on the web server.
For pages with many output combinations, two-phase content generation is not an optimal solution. The reason is that each generated ASP must be individually compiled and cached by the ASP engine (typically several instances of each page are cached on the web server), and each cached ASP instance occupies 3 to 5 MB of RAM. To illustrate, consider an e-commerce site with 100 products. If two-phase content generation is used on the product page, 100 different ASPs are generated. Each ASP must be individually compiled and cached, which wastes CPU and memory resources. A better choice for non-static pages with many output combinations is page fragment caching.
To write a two-phase web page, create an APG script that generates an ASP file. The APG script can contain HTML content, APG script blocks, and ASP script blocks. Then, determine what will cause the APG script to be executed, and write code to execute the APG script when the event(s) occur.
When writing a two-phase APG script, you will need to consider whether individual blocks within a page should be made first or second phase blocks. Here are some pointers to help you make this decision:
Output,
Script, etc.) can only be used within first phase blocks.
Request,
Response, etc.) can only be used within second phase blocks.
Request,
Server, and
Session objects:
<%
Option Explicit
Dim sEquipmentType, sEquipmentTypeTitle, sTitleGIF
sEquipmentType = Request.QueryString("EquipmentType")
Select Case sEquipmentType
Case "Camping"
sEquipmentTypeTitle = "Camping Equipment"
sTitleGIF = "camping"
. . .
End Select
Dim cn, sSQL, rs
Set cn = Server.CreateObject("ADODB.Connection")
sSQL = "SELECT * FROM " & sEquipmentType & "TopSales"
cn.Open Session("ConnectionString")
Set rs = cn.Execute(sSQL)
%>
APGen/Examples/awe_asp/equip.asp. For the optimized version, see
APGen/Examples/awe_apg/apg/equip.apg. The
Server.CreateObject() call can be converted to
Script.CreateObject(), the
Request.QueryString reference can be converted to
Script.Arguments, and the
Session() collection reference can be replaced with an
APGen() collection reference or a global variable. The
Session reference can be replaced because the data being accessed (a connection string) is not user specific. If the data was user specific (like
Session("ItemCount") - the number of items in the shopper's basket), then it would have to be contained in a second phase block.
This example is from an e-commerce site that uses Microsoft Site Server Commerce Edition 3.0. Due to the size and complexity of the page, only part of the page is shown, and the include files' code is not displayed. This example is intended to show how a two-phase page is executed in two separate phases. This example is not intended to explain every detail of this particular page.
The example APG script is named
basket.apg:
<!-- #include apg="include/shop_new.apg" -->
<!-- #include apg="include/header.apg" -->
<!-- #include apg="include/footer.apg" -->
<%#
'############################################################################
' FILE: basket.apg
'
' Copyright (C) 2000 WebGecko Software. All Rights Reserved.
'############################################################################
Output.Filename = "basket.asp"
#%>
<!-- #include file="include/shop.asp" -->
<!-- #include file="include/ado.asp" -->
<!-- #include file="include/util.asp" -->
<% Response.ExpiresAbsolute=#Jan 01, 1980 00:00:00# %>
<%
REM Run the basic plan
Dim nItemCount
Dim mscsOrderForm
Set mscsOrderForm = UtilRunBasket(mscsShopperId)
nItemCount = mscsOrderForm.Items.Count
%><HTML>
<HEAD>
<TITLE>Shopping Basket</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="<%# = color_text #%>" LINK="<%# = color_link #%>" VLINK="<%# = color_vlink #%>" ALINK="<%# = color_alink #%>"BACKGROUND="<%# = background #%>">
<OBJECT RUNAT=Server ID=errorList PROGID="Commerce.SimpleList"></OBJECT>
<%#
Call OutputHeader( "heading_basket.gif", "Shopping Basket" )
#%>
<% if nItemCount = 0 then %>
<BLOCKQUOTE><STRONG>Your basket is empty.</STRONG></BLOCKQUOTE>
<P>
<% else %>
You have <% = nItemCount %> item<% if nItemCount > 1 then %>s<% end if %> in your basket.
<P>
To change or remove an item in the basket, click on its description.
<P>
<TABLE WIDTH=600>
. . .
After the APG script is written, it must be executed - in this case an administrator ASP page executes
basket.apg. (For more information, see Executing APG Scripts.) When
basket.apg is executed, the APG script blocks are executed and
basket.asp is generated:
<!-- #include file="include/shop.asp" -->
<!-- #include file="include/ado.asp" -->
<!-- #include file="include/util.asp" -->
<% Response.ExpiresAbsolute=#Jan 01, 1980 00:00:00# %>
<%
REM Run the basic plan
Dim nItemCount
Dim mscsOrderForm
Set mscsOrderForm = UtilRunBasket(mscsShopperId)
nItemCount = mscsOrderForm.Items.Count
%><HTML>
<HEAD>
<TITLE>Shopping Basket</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="#51392B" LINK="#CC9966" VLINK= "#FF0000"ALINK="#00FF00"BACKGROUND="assets/images/main.gif">
<OBJECT RUNAT=Server ID=errorList PROGID="Commerce.SimpleList"></OBJECT>
<TABLE CELLPADDING="2" CELLSPACING="2" WIDTH="600" BORDER="1" RULES="none" background="http://server/vcapgen/assets/images/navbar.gif?">
<TR>
<TD ALIGN=LEFT WIDTH=115><A HREF="http://server/vcapgen/home.asp"><IMG SRC="http://server/vcapgen/assets/images/logo.gif?" WIDTH=98 HEIGHT=76 BORDER=0 ALT="Click here to return Home" ALIGN=TOP></A></TD>
<% If Not IsEmpty(mscsShopperId) Then %>
<TD ALIGN=CENTER>
<A HREF="http://server/vcapgen/listing.asp"><IMG SRC="http://server/vcapgen/assets/images/button_catalog_up.gif?" BORDER=0 ALT="Store Directory" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/search.asp"><IMG SRC="http://server/vcapgen/assets/images/button_search_up.gif?" BORDER=0 ALT="Search" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/basket.asp"><IMG SRC="http://server/vcapgen/assets/images/button_basket_up.gif?" BORDER=0 ALT="Basket" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/checkout-ship.asp"><IMG SRC="http://server/vcapgen/assets/images/button_ready2pay_up.gif?" BORDER=0 ALT="Pay" ALIGN=CENTER></A>
</TD>
<% End If %>
</TR>
</TABLE>
<TABLE WIDTH=600>
<TR ALIGN=CENTER><TD><BR><IMG SRC="http://server/vcapgen/assets/images/heading_basket.gif?" BORDER="0" ALT="Shopping Basket" ALIGN=CENTER><BR></TD></TR>
</TABLE>
<% if mscsOrderForm.[_Basket_Errors].Count > 0 then %>
<UL>
<% for each errorStr in mscsOrderForm.[_Basket_Errors] %>
<li><% = errorStr %></li>
<% next %>
</UL>
<P>
<% end if %>
<% if nItemCount = 0 then %>
<BLOCKQUOTE><STRONG>Your basket is empty.</STRONG></BLOCKQUOTE>
<P>
<% else %>
You have <% = nItemCount %> item<% if nItemCount > 1 then %>s<% end if %> in your basket.
<P>
To change or remove an item in the basket, click on its description.
<P>
<TABLE WIDTH=600>
. . .
When
basket.asp is requested by a user, the ASP code is executed and a web page response like this is returned:
<HTML>
<HEAD>
<TITLE>Shopping Basket</TITLE>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
</HEAD>
<BODY TEXT="#51392B" LINK="#CC9966" VLINK= "#FF0000"ALINK="#00FF00"BACKGROUND="assets/images/main.gif">
<TABLE CELLPADDING="2" CELLSPACING="2" WIDTH="600" BORDER="1" RULES="none" background="http://server/vcapgen/assets/images/navbar.gif?">
<TR>
<TD ALIGN=LEFT WIDTH=115><A HREF="http://server/vcapgen/home.asp"><IMG SRC="http://server/vcapgen/assets/images/logo.gif?" WIDTH=98 HEIGHT=76 BORDER=0 ALT="Click here to return Home" ALIGN=TOP></A></TD>
<TD ALIGN=CENTER>
<A HREF="http://server/vcapgen/listing.asp"><IMG SRC="http://server/vcapgen/assets/images/button_catalog_up.gif?" BORDER=0 ALT="Store Directory" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/search.asp"><IMG SRC="http://server/vcapgen/assets/images/button_search_up.gif?" BORDER=0 ALT="Search" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/basket.asp"><IMG SRC="http://server/vcapgen/assets/images/button_basket_up.gif?" BORDER=0 ALT="Basket" ALIGN=CENTER></A>
<A HREF="http://server/vcapgen/checkout-ship.asp"><IMG SRC="http://server/vcapgen/assets/images/button_ready2pay_up.gif?" BORDER=0 ALT="Pay" ALIGN=CENTER></A>
</TD>
</TR>
</TABLE>
<TABLE WIDTH=600>
<TR ALIGN=CENTER><TD><BR><IMG SRC="http://server/vcapgen/assets/images/heading_basket.gif?" BORDER="0" ALT="Shopping Basket" ALIGN=CENTER><BR></TD></TR>
</TABLE>
You have 2 items in your basket.
<P>
To change or remove an item in the basket, click on its description.
<P>
<TABLE WIDTH=600>
. . .
Often an existing ASP web page will need to be optimized using two-phase content generation. The following steps describe the tasks involved in optimizing ASP pages.
Keep in mind that the goal of two-phase content generation is to move as much code as possible from the second phase into the first phase. This provides maximal performance and reliability benefits.
Optimization using two-phase content generation typically involves these steps:
Output.Filename - the root APG script should specify the
Output.Filename.
#include files.
#include files should not specify an
Output.Filename.
The ASPToAPG Example can automate steps 3 and 4 for you, but it converts all ASP code to APG code. This is useful for generating static pages, but does not automatically provide the best two-phase page code.
For best results, we recommend manual conversion of two-phase pages and minor re-engineering. Most pages will contain some code that should be left as ASP code, and some code that should be converted to APG script. The decision as to what should be converted is best made by a developer familiar with the page.