On code section that rezzes single objects and attachments, reduce CPU use by reading asset XML a single time with a stream reader rather than multiple times.

Reading large XML documents (e.g. complex attachments) is CPU expensive - this must be done as few times as possible (preferably just once).
Reading these documents into XmlDocument is also more resource intensive than using XmlTextReader, as per Microsoft's own publication "Improve .NET Application Performance and Scalability"
Optimization of other cases will follow if this change is successful.
0.8-extended
Justin Clark-Casey (justincc) 2014-08-28 18:15:33 +01:00 committed by Justin Clark-Casey
parent 5614b28886
commit 5eb6b14854
5 changed files with 127 additions and 70 deletions

View File

@ -829,7 +829,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0); byte bRayEndIsIntersection = (byte)(RayEndIsIntersection ? 1 : 0);
Vector3 pos; Vector3 pos;
bool single = m_Scene.GetObjectsToRez(rezAsset.Data, attachment, out objlist, out veclist, out bbox, out offsetHeight); bool single
= m_Scene.GetObjectsToRez(
rezAsset.Data, attachment, out objlist, out veclist, out bbox, out offsetHeight);
if (single) if (single)
{ {

View File

@ -111,6 +111,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
InventoryFolderBase objsFolder InventoryFolderBase objsFolder
= InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, m_userId, "Objects")[0]; = InventoryArchiveUtils.FindFoldersByPath(m_scene.InventoryService, m_userId, "Objects")[0];
item1.Folder = objsFolder.ID; item1.Folder = objsFolder.ID;
item1.Flags |= (uint)InventoryItemFlags.ObjectHasMultipleItems;
m_scene.AddInventoryItem(item1); m_scene.AddInventoryItem(item1);
SceneObjectGroup so SceneObjectGroup so

View File

@ -30,6 +30,7 @@ using System.Collections.Generic;
using System.Collections; using System.Collections;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using System.Threading;
using System.Timers; using System.Timers;
using System.Xml; using System.Xml;
using OpenMetaverse; using OpenMetaverse;
@ -2192,34 +2193,45 @@ namespace OpenSim.Region.Framework.Scenes
/// Returns one object if the asset is a regular object, and multiple objects for a coalesced object. /// Returns one object if the asset is a regular object, and multiple objects for a coalesced object.
/// </remarks> /// </remarks>
/// <param name="assetData">Asset data</param> /// <param name="assetData">Asset data</param>
/// <param name="attachment">Whether the item is an attachment</param> /// <param name="isAttachment">True if the object is an attachment.</param>
/// <param name="objlist">The objects included in the asset</param> /// <param name="objlist">The objects included in the asset</param>
/// <param name="veclist">Relative positions of the objects</param> /// <param name="veclist">Relative positions of the objects</param>
/// <param name="bbox">Bounding box of all the objects</param> /// <param name="bbox">Bounding box of all the objects</param>
/// <param name="offsetHeight">Offset in the Z axis from the centre of the bounding box /// <param name="offsetHeight">Offset in the Z axis from the centre of the bounding box
/// to the centre of the root prim (relevant only when returning a single object)</param> /// to the centre of the root prim (relevant only when returning a single object)</param>
/// <returns>true = returning a single object; false = multiple objects</returns> /// <returns>
public bool GetObjectsToRez(byte[] assetData, bool attachment, out List<SceneObjectGroup> objlist, out List<Vector3> veclist, /// true if returning a single object or deserialization fails, false if returning the coalesced
/// list of objects
/// </returns>
public bool GetObjectsToRez(
byte[] assetData, bool isAttachment, out List<SceneObjectGroup> objlist, out List<Vector3> veclist,
out Vector3 bbox, out float offsetHeight) out Vector3 bbox, out float offsetHeight)
{ {
objlist = new List<SceneObjectGroup>(); objlist = new List<SceneObjectGroup>();
veclist = new List<Vector3>(); veclist = new List<Vector3>();
XmlDocument doc = new XmlDocument();
string xmlData = Utils.BytesToString(assetData); string xmlData = Utils.BytesToString(assetData);
doc.LoadXml(xmlData);
XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject");
if (e == null || attachment) // Single try
{ {
SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(xmlData); using (XmlTextReader reader = new XmlTextReader(xmlData, XmlNodeType.Element, null))
{
reader.Read();
bool isSingleObject = reader.Name != "CoalescedObject";
if (isSingleObject || isAttachment)
{
SceneObjectGroup g = SceneObjectSerializer.FromOriginalXmlFormat(reader);
objlist.Add(g); objlist.Add(g);
veclist.Add(new Vector3(0, 0, 0)); veclist.Add(Vector3.Zero);
bbox = g.GetAxisAlignedBoundingBox(out offsetHeight); bbox = g.GetAxisAlignedBoundingBox(out offsetHeight);
return true; return true;
} }
else else
{ {
XmlDocument doc = new XmlDocument();
doc.LoadXml(xmlData);
XmlElement e = (XmlElement)doc.SelectSingleNode("/CoalescedObject");
XmlElement coll = (XmlElement)e; XmlElement coll = (XmlElement)e;
float bx = Convert.ToSingle(coll.GetAttribute("x")); float bx = Convert.ToSingle(coll.GetAttribute("x"));
float by = Convert.ToSingle(coll.GetAttribute("y")); float by = Convert.ToSingle(coll.GetAttribute("y"));
@ -2243,10 +2255,23 @@ namespace OpenSim.Region.Framework.Scenes
float z = Convert.ToSingle(rawZ); float z = Convert.ToSingle(rawZ);
veclist.Add(new Vector3(x, y, z)); veclist.Add(new Vector3(x, y, z));
} }
}
return false; return false;
} }
}
}
catch (Exception e)
{
m_log.Error(
"[AGENT INVENTORY]: Deserialization of xml failed when looking for CoalescedObject tag. Exception ",
e);
bbox = Vector3.Zero;
offsetHeight = 0;
}
return true;
}
/// <summary> /// <summary>
/// Event Handler Rez an object into a scene /// Event Handler Rez an object into a scene

View File

@ -902,6 +902,34 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
public void LoadScriptState(XmlTextReader reader)
{
// m_log.DebugFormat("[SCENE OBJECT GROUP]: Looking for script state for {0} in {1}", Name);
while (reader.ReadToFollowing("SavedScriptState"))
{
// m_log.DebugFormat("[SCENE OBJECT GROUP]: Loading script state for {0}", Name);
if (m_savedScriptState == null)
m_savedScriptState = new Dictionary<UUID, string>();
string uuid = reader.GetAttribute("UUID");
if (uuid != null)
{
// m_log.DebugFormat("[SCENE OBJECT GROUP]: Found state for item ID {0} in object {1}", uuid, Name);
UUID itemid = new UUID(uuid);
if (itemid != UUID.Zero)
m_savedScriptState[itemid] = reader.ReadInnerXml();
}
else
{
m_log.WarnFormat("[SCENE OBJECT GROUP]: SavedScriptState element had no UUID in object {0}", Name);
}
}
}
/// <summary> /// <summary>
/// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes. /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes.
/// </summary> /// </summary>

View File

@ -58,58 +58,59 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
/// <param name="xmlData"></param> /// <param name="xmlData"></param>
/// <returns>The scene object deserialized. Null on failure.</returns> /// <returns>The scene object deserialized. Null on failure.</returns>
public static SceneObjectGroup FromOriginalXmlFormat(string xmlData) public static SceneObjectGroup FromOriginalXmlFormat(string xmlData)
{
using (XmlTextReader reader = new XmlTextReader(xmlData, XmlNodeType.Element, null))
return FromOriginalXmlFormat(reader);
}
/// <summary>
/// Deserialize a scene object from the original xml format
/// </summary>
/// <param name="xmlData"></param>
/// <returns>The scene object deserialized. Null on failure.</returns>
public static SceneObjectGroup FromOriginalXmlFormat(XmlTextReader reader)
{ {
//m_log.DebugFormat("[SOG]: Starting deserialization of SOG"); //m_log.DebugFormat("[SOG]: Starting deserialization of SOG");
//int time = System.Environment.TickCount; //int time = System.Environment.TickCount;
SceneObjectGroup sceneObject = null;
try try
{ {
StringReader sr;
XmlTextReader reader;
XmlNodeList parts;
XmlDocument doc;
int linkNum; int linkNum;
doc = new XmlDocument(); reader.ReadToFollowing("RootPart");
doc.LoadXml(xmlData); reader.ReadToFollowing("SceneObjectPart");
parts = doc.GetElementsByTagName("RootPart"); sceneObject = new SceneObjectGroup(SceneObjectPart.FromXml(reader));
reader.ReadToFollowing("OtherParts");
if (parts.Count == 0) if (reader.ReadToDescendant("Part"))
throw new Exception("Invalid Xml format - no root part"); {
do
sr = new StringReader(parts[0].InnerXml); {
reader = new XmlTextReader(sr); if (reader.ReadToDescendant("SceneObjectPart"))
SceneObjectGroup sceneObject = new SceneObjectGroup(SceneObjectPart.FromXml(reader));
reader.Close();
sr.Close();
parts = doc.GetElementsByTagName("Part");
for (int i = 0; i < parts.Count; i++)
{ {
sr = new StringReader(parts[i].InnerXml);
reader = new XmlTextReader(sr);
SceneObjectPart part = SceneObjectPart.FromXml(reader); SceneObjectPart part = SceneObjectPart.FromXml(reader);
linkNum = part.LinkNum; linkNum = part.LinkNum;
sceneObject.AddPart(part); sceneObject.AddPart(part);
part.LinkNum = linkNum; part.LinkNum = linkNum;
part.TrimPermissions(); part.TrimPermissions();
reader.Close(); }
sr.Close(); }
while (reader.ReadToNextSibling("Part"));
} }
// Script state may, or may not, exist. Not having any, is NOT // Script state may, or may not, exist. Not having any, is NOT
// ever a problem. // ever a problem.
sceneObject.LoadScriptState(doc); sceneObject.LoadScriptState(reader);
return sceneObject;
} }
catch (Exception e) catch (Exception e)
{ {
m_log.ErrorFormat( m_log.ErrorFormat("[SERIALIZER]: Deserialization of xml failed. Exception {0}", e);
"[SERIALIZER]: Deserialization of xml failed with {0}. xml was {1}", e, xmlData);
return null; return null;
} }
return sceneObject;
} }
/// <summary> /// <summary>