Atsushi & Gonzalo (and anyone else),

Attached are two patches to reduce new object creations.

In Sys.Xml, I cut out string concatenations in XmlElement.Name and
XmlAttribute.Name.  The (prefix + ":" + localname) concatenation
occurred even if the final string was already in the NameTable.  To
avoid this, I added a method to NameTable (an internal class) to accept
a prefix and localname separately for checking if the final concatenated
string would be in the name table already.  The only performance hits
might come from calls to the 'is' operator and two calls to
String.CompareOrdinal rather than a single call to Equals.

In Sys.Web.HttpWriter, a byte[] is created on each call to Write for
strings and char arrays.  Instead, I reuse and resize a byte[] array
kept by the class.  Since HttpWriter isn't thread safe, this seems ok.
The downside is that string encoding is done through two calls, once to
get the byte count and then to get the bytes.  I don't know what kind of
performance impact that might have.

These changes reduce the two biggest object allocators per request for
my ASP.NET site.  My hope is that this will cut down on the runaway
memory allocation that I'm still facing and hopefully have no
significant performance cost.  But I don't know about either.

I'll try the patches out for a while to make sure I didn't goof anything up.

Thanks guys.

-- 
- Joshua Tauberer

http://taubz.for.net

"Unfortunately, we're having this discussion. It's too bad,
because guess who listens to the discussion: the enemy."
Index: ../System.Web/System.Web/ChangeLog
===================================================================
--- ../System.Web/System.Web/ChangeLog	(revision 58623)
+++ ../System.Web/System.Web/ChangeLog	(working copy)
@@ -1,3 +1,8 @@
+2006-03-27 Joshua Tauberer <[EMAIL PROTECTED]>
+
+	* HttpWriter.cs: Avoid creation of a byte[] on each Write()
+	  by reusing and resizing a private array.
+
 2006-03-23 Gonzalo Paniagua Javier <[EMAIL PROTECTED]>
 
 	* HttpResponse.cs: more fixes for CacheControl: MS allows to set it to
Index: ../System.Web/System.Web/HttpWriter.cs
===================================================================
--- ../System.Web/System.Web/HttpWriter.cs	(revision 58623)
+++ ../System.Web/System.Web/HttpWriter.cs	(working copy)
@@ -42,6 +42,8 @@
 		HttpResponseStream output_stream;
 		HttpResponse response;
 		Encoding encoding;
+		
+		byte[] bytebuffer = new byte[2048];
 
 		internal HttpWriter (HttpResponse response)
 		{
@@ -97,23 +99,20 @@
 		
 		public override void Write (string s)
 		{
-			if (s == null)
-				return;
-			
-			byte [] xx = encoding.GetBytes (s);
-
-			output_stream.Write (xx, 0, xx.Length);
-			
-			if (response.buffer)
-				return;
-
-			response.Flush ();
+			WriteString (s, 0, s.Length);
 		}
 		
 		public override void Write (char [] buffer, int index, int count)
 		{
-			byte [] xx = encoding.GetBytes (buffer, index, count);
-			output_stream.Write (xx, 0, xx.Length);
+			/*byte [] xx = encoding.GetBytes (buffer, index, count);
+			output_stream.Write (xx, 0, xx.Length);*/
+			
+			int length = encoding.GetByteCount (buffer, index, count);
+			if (length > bytebuffer.Length)
+				bytebuffer = new byte[length << 2];
+			encoding.GetBytes (buffer, index, count, bytebuffer, 0);
+			
+			output_stream.Write (bytebuffer, 0, length);
 
 			if (response.buffer)
 				return;
@@ -135,12 +134,22 @@
 
 		public void WriteString (string s, int index, int count)
 		{
-			char [] a = s.ToCharArray (index, count);
+			if (s == null)
+				return;
 
-			byte [] xx = encoding.GetBytes (a, 0, count);
+			int length;
+			if (index == 0 && count == s.Length) {
+				length = encoding.GetByteCount (s); 
+			} else {
+				char [] chars = s.ToCharArray(index, count);
+				length = encoding.GetByteCount (chars);
+			}
+			if (length > bytebuffer.Length)
+				bytebuffer = new byte[length << 2];
+			encoding.GetBytes (s, index, count, bytebuffer, 0);
 			
-			output_stream.Write (xx, 0, xx.Length);
-
+			output_stream.Write (bytebuffer, 0, length);
+			
 			if (response.buffer)
 				return;
 
Index: System.Xml/XmlElement.cs
===================================================================
--- System.Xml/XmlElement.cs	(revision 58622)
+++ System.Xml/XmlElement.cs	(working copy)
@@ -179,10 +179,7 @@
 
 		public override string Name {
 			get {
-				if (name.Prefix == String.Empty || name.Prefix == null)
-					return name.LocalName;
-				else
-					return OwnerDocument.NameTable.Add (name.Prefix + ":" + name.LocalName);
+				return name.GetQName (OwnerDocument.NameTable);
 			}
 		}
 
Index: System.Xml/XmlNameEntry.cs
===================================================================
--- System.Xml/XmlNameEntry.cs	(revision 58622)
+++ System.Xml/XmlNameEntry.cs	(working copy)
@@ -52,6 +52,8 @@
 		public string LocalName;
 		public string NS;
 		public int Hash;
+		
+		string CachedQName;
 
 		public override bool Equals (object other)
 		{
@@ -66,5 +68,17 @@
 		{
 			return Hash;
 		}
+		
+		public string GetQName(XmlNameTable nametable) {
+			if (CachedQName == null) {
+				if (Prefix == null || Prefix == String.Empty)
+					CachedQName = LocalName;
+				else if (nametable is NameTable)
+					CachedQName = ((NameTable)nametable).AddQName (Prefix, LocalName);
+				else
+					CachedQName = nametable.Add (Prefix + ":" + LocalName);
+			}
+			return CachedQName;
+		}
 	}
 }
Index: System.Xml/ChangeLog
===================================================================
--- System.Xml/ChangeLog	(revision 58622)
+++ System.Xml/ChangeLog	(working copy)
@@ -1,3 +1,14 @@
+2006-03-27  Joshua Tauberer <[EMAIL PROTECTED]>
+
+	* NameTable.cs: Added a new function AddQName which adds a string
+	  like Add but taking a prefix and localname as arguments, so that
+	  they don't have to be concatenated before the call.  AddQName
+	  won't do a concatenation if the string is already in the table.
+	* XmlElement.cs, XmlAtribute.cs: Avoid string creations in their
+	  Name get accessors for prefix + ":" + localname.
+	* XmlNameEntry.cs: To create a QName, use NameTable's AddQName method
+	  (if the name table is a NameTable), and cache the result.
+
 2006-03-23  Atsushi Enomoto <[EMAIL PROTECTED]>
 
 	* XmlReader.cs : in XmlReader.Create() ValidationType Auto and XDR
Index: System.Xml/XmlAttribute.cs
===================================================================
--- System.Xml/XmlAttribute.cs	(revision 58622)
+++ System.Xml/XmlAttribute.cs	(working copy)
@@ -157,7 +157,7 @@
 
 		public override string Name {
 			get { 
-				return name.Prefix != String.Empty ? OwnerDocument.NameTable.Add (name.Prefix + ":" + name.LocalName) : name.LocalName;
+				return name.GetQName (OwnerDocument.NameTable);
 			}
 		}
 
Index: System.Xml/NameTable.cs
===================================================================
--- System.Xml/NameTable.cs	(revision 58622)
+++ System.Xml/NameTable.cs	(working copy)
@@ -110,6 +110,36 @@
 			return AddEntry (key, h);
 		}
 
+		internal string AddQName (string prefix, string localname)
+		{
+			if (prefix == null || prefix == String.Empty || localname == null || localname == String.Empty) throw new ArgumentNullException ();
+
+			int keyLen = prefix.Length + 1 + localname.Length;
+
+			int h = 0;
+			// This is from the String.Gethash () icall
+			int pl = prefix.Length;
+			int ll = localname.Length;
+			for (int i = 0; i < pl; i++)
+				h = (h << 5) - h + prefix [i];
+			h = (h << 5) - h + ':';
+			for (int i = 0; i < ll; i++)
+				h = (h << 5) - h + localname [i];
+			
+			// h must be be >= 0
+			h &= 0x7FFFFFFF;
+
+			for (Entry e = buckets [h % count]; e != null; e = e.next) {
+				if (e.hash == h && e.len == keyLen
+					&& String.CompareOrdinal (e.str, 0, prefix, 0, pl) == 0
+					&& e.str [pl] == ';'
+					&& String.CompareOrdinal (e.str, pl+1, localname, 0, ll) == 0)
+					return e.str;
+			}
+			
+			return AddEntry (prefix + ":" + localname, h);
+		}
+
 		public override string Get (char [] key, int start, int len)
 		{
 			if (((0 > start) && (start >= key.Length))
_______________________________________________
Mono-devel-list mailing list
Mono-devel-list@lists.ximian.com
http://lists.ximian.com/mailman/listinfo/mono-devel-list

Reply via email to