I’ve moved
August 17th, 2009Previously Nexus related along with ASP.NET development entries have been mixed in this blog.
With effect from Aug 2009, all non nexus related blog posts can now be found at http://jefferytay.wordpress.com
Previously Nexus related along with ASP.NET development entries have been mixed in this blog.
With effect from Aug 2009, all non nexus related blog posts can now be found at http://jefferytay.wordpress.com
If you have a TabContainer with multiple Tabs, and more than 1 tab have a gridview, any postback actions on the gridview will cause your tabcontainer to jump back to the first tab if you are not did not enclose the TabContainer in an update panel.
Digging into the source code reveals that the ClientStateField property is not updated each time a tab new tab is selected.
To workaround this, include the following javascript into the head of the document
function ActiveTabChanged(sender, e) {
sender.get_clientStateField().value = sender.saveClientState();
}
and inside the TabContainer set the OnClientActiveTabChanged="ActiveTabChanged"
Based on a Vladimir Bodurov blog post on IDictionary options – performance test – SortedList vs. SortedDictionary vs. Dictionary vs. Hashtable, it seems to indicate that SortedDictionary is slower during inserts as compared to Dictionary but faster during searching.
However during the course of my development in ASP.NET 3.5, there have been several occasions which seems to contradict this behaviour, perhaps it is because he is using WinForms and i’m using ASP.NET. So i took his code, did some modification for it to run several rounds and in IIS and went off from there (The source code can be found at the bottom of the page)
The test procedure is exactly the same as what he did, except since its only 2 i did 2 sets of 20, First doing SortedDictionary then Dictionary (Test result 1 to 20) and the second set doing Dictionary then SortedDictionary (Test result 21 to 40).
The averages of both set do seem to indicate negligible differences in the execution sequence.
The results are extremely interesting. Dictionary outperforms SortedDictionary on all counts! Memory usage, insertion time and search time are all much lower for Dictionary compared to SortedDictionary!
Not really sure why this is so, but if i remember correctly Dictionary uses hashes, so my best guess is it is easier to search for a hash as compared to string comparison..
The generated raw results from the web application
| Test | Memory | Insert (Time taken in Ticks) | Search (Time taken in Ticks) | |||
| Dictionary | Dictionary | Dictionary | ||||
| 1 | 53919472 | 53376100 | 48921833 | 13100780 | 488 | 132 |
| 2 | 53919484 | 53376100 | 47371977 | 12744252 | 647 | 72 |
| 3 | 53919468 | 53376088 | 47386173 | 12994452 | 209 | 69 |
| 4 | 53919340 | 53376124 | 47428627 | 15464752 | 239 | 71 |
| 5 | 53919484 | 53376112 | 47508765 | 13040552 | 211 | 68 |
| 6 | 53919644 | 53376100 | 48597956 | 13617493 | 216 | 71 |
| 7 | 53919484 | 53376100 | 49073226 | 13166390 | 404 | 71 |
| 8 | 53919484 | 53376100 | 48807615 | 13104169 | 229 | 69 |
| 9 | 53919484 | 53376088 | 48633159 | 12904931 | 278 | 89 |
| 10 | 53919484 | 53376088 | 49147736 | 13043290 | 243 | 72 |
| 11 | 53919484 | 53376100 | 48755009 | 12901049 | 221 | 68 |
| 12 | 53919484 | 53376100 | 48720694 | 12851970 | 216 | 65 |
| 13 | 53919484 | 53376088 | 48968614 | 13938456 | 278 | 76 |
| 14 | 53919484 | 53376124 | 47349876 | 13072479 | 232 | 69 |
| 15 | 53917592 | 53376112 | 47436475 | 13217946 | 374 | 263 |
| 16 | 53919496 | 53376100 | 47271521 | 12936580 | 279 | 85 |
| 17 | 53919484 | 53376088 | 49116031 | 14775245 | 228 | 64 |
| 18 | 53919496 | 53376076 | 47675434 | 13543449 | 258 | 79 |
| 19 | 53919496 | 53376088 | 47454095 | 13330955 | 219 | 62 |
| 20 | 53919484 | 53376100 | 49053229 | 12932112 | 236 | 73 |
| Average (Set 1) | 53919390.6 | 53376098.8 | 48233902.25 | 13334065.1 | 285.25 | 84.4 |
| 21 | 53376052 | 35231348 | 9248780 | 245 | 108 | |
| 22 | 53919544 | 53376064 | 35237264 | 8211268 | 183 | 312 |
| 23 | 53919544 | 53376052 | 35354053 | 8161178 | 170 | 82 |
| 24 | 53919516 | 53376040 | 35166182 | 8433640 | 248 | 117 |
| 25 | 53919984 | 53376000 | 35340838 | 8559027 | 161 | 92 |
| 26 | 53919532 | 53376064 | 35446193 | 8515937 | 190 | 87 |
| 27 | 53919532 | 53376040 | 35425444 | 8307412 | 301 | 89 |
| 28 | 53919484 | 53376124 | 35207419 | 8468252 | 173 | 88 |
| 29 | 53919532 | 53376064 | 35275126 | 8100160 | 326 | 193 |
| 30 | 53919532 | 53376064 | 35484239 | 8119661 | 188 | 86 |
| 31 | 53919532 | 53376064 | 35604412 | 8484580 | 180 | 82 |
| 32 | 53919532 | 53376052 | 35550023 | 8283421 | 247 | 108 |
| 33 | 53919532 | 53376076 | 35213423 | 8398157 | 187 | 85 |
| 34 | 53919484 | 53376136 | 35264521 | 8527381 | 183 | 89 |
| 35 | 53919484 | 53376136 | 35383377 | 8723896 | 179 | 84 |
| 36 | 53919484 | 53376100 | 35232178 | 8567521 | 162 | 83 |
| 37 | 53919544 | 53376064 | 35054454 | 8132677 | 174 | 80 |
| 38 | 53919532 | 53376052 | 35188672 | 8193882 | 184 | 86 |
| 39 | 53919496 | 53376136 | 35265145 | 8709565 | 179 | 103 |
| 40 | 53919532 | 53376064 | 35123234 | 8095966 | 199 | 87 |
| Average (Set 2) | 53919542.4 | 53376072.2 | 35302377.25 | 8412118.05 | 202.95 | 107.05 |
| Average | 53919466.5 | 53376085.5 | 41768139.75 | 10873091.58 | 244.1 | 95.725 |
Source Code (.aspx.cs)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;
using System.Data;
public partial class test_cittka : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
DataTable tbl = new DataTable();
tbl.Columns.Add(”Memory_SortedDictionary”);
tbl.Columns.Add(”Memory_Dictionary”);
tbl.Columns.Add(”Insert_SortedDictionary”);
tbl.Columns.Add(”Insert_Dictionary”);
tbl.Columns.Add(”Search_SortedDictionary”);
tbl.Columns.Add(”Search_Dictionary”);
for (int i = 0; i < 20; i++)
{
DataRow row = tbl.NewRow();
sortedDictionary = new SortedDictionary<string, string>();
dictionary = new Dictionary<string, string>();
Insert_Test(”SortedDictionary”, sortedDictionary, row, 0, 2);
Insert_Test(”Dictionary”, dictionary, row, 1,3);
Search_Test(”SortedDictionary”, sortedDictionary, row,4);
Search_Test(”Dictionary”, dictionary,row,5);
tbl.Rows.Add(row);
}
GridView gv = new GridView();
gv.DataSource = tbl;
gv.DataBind();
Page.Form.Controls.Add(gv);
}
private readonly int searchIndex = 88888;
private readonly int numberRepetition = 500000;
private SortedDictionary<string, string> sortedDictionary = new SortedDictionary<string, string>();
private Dictionary<string, string> dictionary = new Dictionary<string, string>();
private void Insert_Test(string name, IDictionary<string, string> dict, DataRow row, int idxMemory, int idxInsert)
{
Response.Write(String.Format(”<br>——–Insert {0}——–”, name));
string[] letters = { “A”, “B”, “C”, “D”, “E”, “F”, “G”, “H”, “I”, “J” };
long memoryStart = System.GC.GetTotalMemory(true);
Stopwatch watch = new Stopwatch();
watch.Start();
Random rand = new Random();
for (int i = 0; i < numberRepetition; i++)
{
string key = GetRandomLetter(letters, rand, i) + “_key” + i;
string value = “value” + i;
dict.Add(key, value);
}
long memoryEnd = System.GC.GetTotalMemory(true);
watch.Stop();
Response.Write(String.Format(”<br>Memory Allocated by {0} is: {1}bytes”,
name, memoryEnd – memoryStart));
PrintResults(watch);
row[idxMemory] = (memoryEnd – memoryStart).ToString();
row[idxInsert] = watch.ElapsedTicks.ToString();
}
private void Search_Test(string name, IDictionary<string, string> dict, DataRow row, int idx)
{
Stopwatch watch = new Stopwatch();
Response.Write(String.Format(”<br>——–Search {0}——–”, name));
watch.Start();
Response.Write(String.Format(”<br>Found:{0}”, dict["A_key" + searchIndex]));
watch.Stop();
PrintResults(watch);
row[idx] = watch.ElapsedTicks.ToString();
}
private void PrintResults(Stopwatch watch)
{
Response.Write(String.Format(”<br>Elapsed: {0}”, watch.Elapsed));
Response.Write(String.Format(”<br>In milliseconds: {0}”, watch.ElapsedMilliseconds));
Response.Write(String.Format(”<br>In timer ticks: {0}”, watch.ElapsedTicks));
}
private string GetRandomLetter(string[] letters, Random rand, int i)
{
if (i == searchIndex)
{
return “A”;
}
return letters[rand.Next(0, 10)];
}
}
This post aims to highlight the differences between the cache implementation of HttpRunTime and EnterpriseLibrary
| HttpRuntime.Cache | Enterprise Library Caching Block | |
| Standard Actions | ||
| Add Item to Cache | ![]() |
![]() |
| Read Item from Cache | ![]() |
![]() |
| Update Item to Cache | (using insert) |
(using add) |
| Delete Item from Cache | ![]() |
![]() |
| Flush all Items in Cache | ![]() |
![]() |
| Enumeration Actions | ||
| Enumerate all items in cache | ![]() |
![]() |
| Employ LINQ Expressions | ![]() |
![]() |
| Cache Dependencies | ||
| Cache Dependency – File | ![]() |
![]() |
| Cache Dependency – Other Cache Item | ![]() |
(write your own extension) |
| Expirations | ||
| Expiration – Absolute Time | ![]() |
![]() |
| Expiration – Sliding Time | ![]() |
![]() |
| Expiration – Extended Formats
* Extended time format “<Minute> <Hour> <Day of month> <Month> <Day of week>” * means run every period Examples o “* * * * *” expires every minute o “5 * * * *” expire 5th minute of every hour o “* 21 * * *” expire every minute of the 21st hour of every day o “31 15 * * *” expire 3:31 PM every day o “7 4 * * 6″ expire Saturday 4:07 AM o “15 21 4 7 *” expire 9:15 PM on 4 July |
![]() |
![]() |
| Others | ||
| Item priority | ![]() |
![]() |
| Run function when item is removed | ![]() |
![]() |
| Get number of items in cache | ![]() |
![]() |
| Limit number of items in cache | ![]() |
![]() |
| Can use persistent Storage | ![]() |
![]() |
| Run function to refresh item when expires | ![]() |
![]() |
When you are running your ASP.NET applications inside an application pool with only 1 worker process, everything works as expected. But things usually do not go so smoothly when you run the same application inside a web garden.
What is a web garden
Simply put, a web garden is a single web server running multiple IIS processes. The good thing about this is, there are multiple process which can service your http/https calls. So if a client is holding up one process, the other processes can still continue. Also if a particular process were to hang and restart itself, your website will still continue to serve clients as the other processes are running.
Read more about Microsoft’s explanation of web garden here
The article clearly states that
all processes have their own copy of application state, in-process session state, caches, and static data
Let us examine each one of these and see what we can do to workaround it
Application state
Problem Statement
As each process has their own application state, whatever you change in one process will not be updated in another process. As this exists purely for backward compatibility with classic ASP applicationstate, Microsoft does not recommend using this alot, especially for read/write operations.
Workaround
Use Cache instead of ApplicationState if you can.
Cached Items
Problem Statement
Be it HttpRunTime.Cache or Enterprise Library Cache, the cache will not reflect latest updates inside a web garden. This is because only the worker process which updated the Cache Item will see the updated values, the other worker processes will not be notified that a change has happened and will continue using the value stored in their in memory cache.
Workaround
There are a few paid solutions, and the only free one so far which is able to address this is can be found at http://enyimmemcached.codeplex.com/Release/ProjectReleases.aspx?ReleaseId=13095 or http://www.codeproject.com/KB/aspnet/memcached_aspnet.asp
Session state
Problem Statement
Using web gardens, you will find that the session values gets rotated around. Again this is due to the fact that different worker process is handling your request, and each worker process has its own session state management.
Workaround
Simply change the inproc to either State Server or SQL Server to store the session state outside of the worker process. Performance will degrade when using either of these 2 options, but at least you can be assured of the persistancy of the data