Browse Source

optimized processing of *Self columns to avoid GUI hangs in SharpDevelop

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@5009 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Siegfried Pammer 16 years ago
parent
commit
e4591410ee
  1. 24
      src/AddIns/Misc/Profiler/Controller/Data/CallTreeNode.cs
  2. 75
      src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs
  3. 21
      src/AddIns/Misc/Profiler/Controller/Data/SQLiteCallTreeNode.cs
  4. 39
      src/AddIns/Misc/Profiler/Controller/Data/UnitTestRootCallTreeNode.cs
  5. 6
      src/AddIns/Misc/Profiler/Controller/Data/UnmanagedCallTreeNode.cs
  6. 16
      src/AddIns/Misc/Profiler/Frontend/Controls/CallTreeNodeViewModel.cs

24
src/AddIns/Misc/Profiler/Controller/Data/CallTreeNode.cs

@ -79,12 +79,17 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -79,12 +79,17 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </summary>
public abstract long CpuCyclesSpent { get; }
long cpuCyclesSpentSelf = -1;
/// <summary>
/// Gets how many CPU cycles were spent inside this method, excluding sub calls.
/// </summary>
public virtual long CpuCyclesSpentSelf {
get {
return GetCpuCyclesSelf();
if (cpuCyclesSpentSelf == -1)
cpuCyclesSpentSelf = GetCpuCyclesSelf();
return cpuCyclesSpentSelf;
}
}
@ -116,11 +121,19 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -116,11 +121,19 @@ namespace ICSharpCode.Profiler.Controller.Data
/// <summary>
/// Determines whether this node is a thread node or not.
/// </summary>
public bool IsThread
{
public bool IsThread {
get { return this.Name.StartsWith("Thread#", StringComparison.Ordinal); }
}
/// <summary>
/// Determines whether this node has chil
/// </summary>
public virtual bool HasChildren {
get {
return this.Children.Any();
}
}
/// <summary>
/// Gets a readonly list of the string representation of the parameters.
/// </summary>
@ -136,6 +149,11 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -136,6 +149,11 @@ namespace ICSharpCode.Profiler.Controller.Data
/// </summary>
public abstract double TimeSpent { get; }
/// <summary>
/// Gets the time spent inside the method (excluding sub calls) in milliseconds.
/// </summary>
public abstract double TimeSpentSelf { get; }
/// <summary>
/// Gets a reference to the parent of this CallTreeNode.
/// </summary>

75
src/AddIns/Misc/Profiler/Controller/Data/ProfilingDataSQLiteProvider.cs

@ -60,35 +60,11 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -60,35 +60,11 @@ namespace ICSharpCode.Profiler.Controller.Data
internal IQueryable<CallTreeNode> GetChildren(SQLiteCallTreeNode parent)
{
SQLiteCommand cmd;
using (LockAndCreateCommand(out cmd)) {
cmd.CommandText = @"SELECT id, nameid, callcount, timespent, isactiveatstart
FROM FunctionData
WHERE parentid IN(" + string.Join(",", parent.ids.Select(a => a.ToString(CultureInfo.InvariantCulture.NumberFormat)).ToArray()) + @")
ORDER BY id;";
using (SQLiteDataReader reader = cmd.ExecuteReader()) {
List<SQLiteCallTreeNode> items = new List<SQLiteCallTreeNode>();
while (reader.Read()) {
int childNameId = reader.GetInt32(1);
SQLiteCallTreeNode newItem = items.Find(node => node.nameId == childNameId);
if (newItem == null) {
newItem = new SQLiteCallTreeNode(childNameId, (parent.nameId == 0) ? null : parent, this);
items.Add(newItem);
// works because of ORDER BY id
newItem.isActiveAtStart = reader.GetBoolean(4);
}
newItem.callCount += reader.GetInt32(2);
newItem.cpuCyclesSpent += (ulong)reader.GetInt64(3);
newItem.ids.Add(reader.GetInt32(0));
}
return items.Cast<CallTreeNode>().AsQueryable();
}
}
string[] ids = parent.ids
.Select(a => a.ToString(CultureInfo.InvariantCulture))
.ToArray();
return GetMergedFunctionData("parentid IN(" + string.Join(",", ids) + ")", parent.selectionStartIndex);
}
internal IQueryable<CallTreeNode> GetCallers(SQLiteCallTreeNode item)
@ -114,6 +90,7 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -114,6 +90,7 @@ namespace ICSharpCode.Profiler.Controller.Data
SQLiteCallTreeNode newItem = items.Find(node => node.nameId == childNameId);
if (newItem == null) {
newItem = new SQLiteCallTreeNode(childNameId, null, this);
newItem.selectionStartIndex = item.selectionStartIndex;
items.Add(newItem);
// works because of ORDER BY id
@ -291,6 +268,8 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -291,6 +268,8 @@ namespace ICSharpCode.Profiler.Controller.Data
using (SQLiteDataReader reader = cmd.ExecuteReader()) {
SQLiteCallTreeNode root = new SQLiteCallTreeNode(0, null, this);
root.selectionStartIndex = startIndex;
while (reader.Read()) {
root.callCount += reader.GetInt32(2);
@ -303,6 +282,7 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -303,6 +282,7 @@ namespace ICSharpCode.Profiler.Controller.Data
}
}
#region Properties
/// <inheritdoc/>
public override void SetProperty(string name, string value)
{
@ -349,6 +329,7 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -349,6 +329,7 @@ namespace ICSharpCode.Profiler.Controller.Data
return null;
}
#endregion
int processorFrequency = -1;
@ -375,35 +356,49 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -375,35 +356,49 @@ namespace ICSharpCode.Profiler.Controller.Data
if (endIndex < startIndex || endIndex >= this.DataSets.Count)
throw new ArgumentOutOfRangeException("endIndex", endIndex, "Value must be between " + startIndex + " and " + (this.DataSets.Count - 1));
IList<CallTreeNode> functions = new List<CallTreeNode>();
var result = GetMergedFunctionData(string.Format("datasetid BETWEEN {0} AND {1}", startIndex, endIndex), startIndex);
return result
.Where(i => i.NameMapping.Id != 0 && !i.NameMapping.Name.StartsWith("Thread#", StringComparison.Ordinal))
.AsQueryable();
}
IQueryable<CallTreeNode> GetMergedFunctionData(string condition, int startIndex)
{
IList<CallTreeNode> result = new List<CallTreeNode>();
SQLiteCommand cmd;
using (LockAndCreateCommand(out cmd)) {
cmd.CommandText = @"SELECT GROUP_CONCAT(id), nameid, SUM(timespent), SUM(callcount)
FROM FunctionData
WHERE datasetid BETWEEN @start AND @end
GROUP BY nameid;";
cmd.Parameters.Add(new SQLiteParameter("@start", startIndex));
cmd.Parameters.Add(new SQLiteParameter("@end", endIndex));
cmd.CommandText = @"SELECT
GROUP_CONCAT(id),
nameid,
SUM(timespent),
SUM(callcount),
MAX(id != endid) AS hasChildren,
SUM((datasetid = " + startIndex + @") AND isActiveAtStart) AS activeCallCount
FROM FunctionData
WHERE " + condition + @"
GROUP BY nameid;";
using (SQLiteDataReader reader = cmd.ExecuteReader()) {
while (reader.Read()) {
SQLiteCallTreeNode node = new SQLiteCallTreeNode(reader.GetInt32(1), null, this);
node.selectionStartIndex = startIndex;
node.callCount = reader.GetInt32(3);
node.cpuCyclesSpent = (ulong)reader.GetInt64(2);
node.ids = reader.GetString(0).Split(',').Select(s => int.Parse(s)).ToList();
node.ids.Sort();
node.hasChildren = reader.GetBoolean(4);
node.activeCallCount = reader.GetInt32(5);
// Can not do filtering of root and thread nodes here,
// because retrieval of names needs access to DB
// which is forbidden now (we are inside the lock!)
functions.Add(node);
result.Add(node);
}
}
}
// Do filtering now via LINQ
return functions.SkipWhile(i => i.NameMapping.Id == 0 || i.NameMapping.Name.StartsWith("Thread#", StringComparison.Ordinal)).AsQueryable();
return result.AsQueryable();
}
LockObject LockAndCreateCommand(out SQLiteCommand cmd)

21
src/AddIns/Misc/Profiler/Controller/Data/SQLiteCallTreeNode.cs

@ -26,6 +26,9 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -26,6 +26,9 @@ namespace ICSharpCode.Profiler.Controller.Data
CallTreeNode parent;
ProfilingDataSQLiteProvider provider;
internal List<int> ids = new List<int>();
internal bool hasChildren;
internal int activeCallCount;
internal int selectionStartIndex;
/// <summary>
/// Creates a new CallTreeNode.
@ -91,6 +94,12 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -91,6 +94,12 @@ namespace ICSharpCode.Profiler.Controller.Data
return CpuCyclesSpent / (1000.0 * this.provider.ProcessorFrequency);
}
}
public override double TimeSpentSelf {
get {
return CpuCyclesSpentSelf / (1000.0 * this.provider.ProcessorFrequency);
}
}
/// <summary>
/// Gets whether the function call started in a previous data set that's not selected.
@ -101,8 +110,12 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -101,8 +110,12 @@ namespace ICSharpCode.Profiler.Controller.Data
}
}
public override int CallCount {
get { return callCount + activeCallCount; }
}
/// <summary>
/// Merges a collection of CallTreeNodes into one CallTreeNode, all valuess are accumulated.
/// Merges a collection of CallTreeNodes into one CallTreeNode, all values are accumulated.
/// </summary>
/// <param name="nodes">The collection of nodes to process.</param>
/// <returns>A new CallTreeNode.</returns>
@ -110,8 +123,10 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -110,8 +123,10 @@ namespace ICSharpCode.Profiler.Controller.Data
{
SQLiteCallTreeNode mergedNode = new SQLiteCallTreeNode(0, null, this.provider);
bool initialised = false;
foreach (SQLiteCallTreeNode node in nodes) {
mergedNode.ids.AddRange(node.ids);
mergedNode.selectionStartIndex = Math.Min(mergedNode.selectionStartIndex, node.selectionStartIndex);
mergedNode.callCount += node.callCount;
mergedNode.cpuCyclesSpent += node.cpuCyclesSpent;
if (!initialised || mergedNode.nameId == node.nameId)
@ -168,5 +183,9 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -168,5 +183,9 @@ namespace ICSharpCode.Profiler.Controller.Data
return hash;
}
public override bool HasChildren {
get { return this.hasChildren; }
}
}
}

39
src/AddIns/Misc/Profiler/Controller/Data/UnitTestRootCallTreeNode.cs

@ -18,7 +18,7 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -18,7 +18,7 @@ namespace ICSharpCode.Profiler.Controller.Data
List<CallTreeNode> unitTests;
/// <summary>
/// Creates a new UnitTestRootCallTreeNode.
/// Creates a new UnitTestRootCallTreeNode.
/// </summary>
public UnitTestRootCallTreeNode(IEnumerable<CallTreeNode> unitTests)
{
@ -46,58 +46,65 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -46,58 +46,65 @@ namespace ICSharpCode.Profiler.Controller.Data
}
}
/// <inheritdoc/>
/// <inheritdoc/>
public override double TimeSpent {
get {
return 0;
}
}
/// <inheritdoc/>
/// <inheritdoc/>
public override int RawCallCount {
get {
return 0;
}
}
/// <inheritdoc/>
/// <inheritdoc/>
public override CallTreeNode Parent {
get {
return null;
}
}
/// <inheritdoc/>
/// <inheritdoc/>
public override CallTreeNode Merge(System.Collections.Generic.IEnumerable<CallTreeNode> nodes)
{
// throw new ShouldNeverHappenException();
throw new NotSupportedException("Cannot merge a UnitTestRootCallTreeNode (should never be possible)");
}
/// <inheritdoc/>
/// <inheritdoc/>
public override int GetHashCode()
{
return this.unitTests.Aggregate(0, (sum, item) => sum ^= item.GetHashCode());
}
/// <inheritdoc/>
/// <inheritdoc/>
public override bool Equals(CallTreeNode other)
{
return (other is UnitTestRootCallTreeNode) && (other as UnitTestRootCallTreeNode).unitTests.SequenceEqual(unitTests);
}
/// <inheritdoc/>
/// <inheritdoc/>
public override IQueryable<CallTreeNode> Callers {
get {
return Enumerable.Empty<CallTreeNode>().AsQueryable();
}
}
/// <inheritdoc/>
/// <inheritdoc/>
public override IQueryable<CallTreeNode> Children {
get {
return unitTests.AsQueryable();
}
}
/// <inheritdoc/>
public override double TimeSpentSelf {
get {
return 0;
}
}
}
}

6
src/AddIns/Misc/Profiler/Controller/Data/UnmanagedCallTreeNode.cs

@ -94,6 +94,12 @@ namespace ICSharpCode.Profiler.Controller.Data @@ -94,6 +94,12 @@ namespace ICSharpCode.Profiler.Controller.Data
return this.CpuCyclesSpent / (1000.0 * this.dataSet.ProcessorFrequency);
}
}
public override double TimeSpentSelf {
get {
return this.CpuCyclesSpentSelf / (1000.0 * this.dataSet.ProcessorFrequency);
}
}
public override CallTreeNode Merge(IEnumerable<CallTreeNode> nodes)
{

16
src/AddIns/Misc/Profiler/Frontend/Controls/CallTreeNodeViewModel.cs

@ -330,20 +330,18 @@ namespace ICSharpCode.Profiler.Controls @@ -330,20 +330,18 @@ namespace ICSharpCode.Profiler.Controls
public string TimeSpentSelf {
get {
if (!node.IsThread) {
double value = node.TimeSpent - node.Children.Aggregate(0.0, (sum, item) => sum + item.TimeSpent);
return value.ToString("f6") + "ms";
} else
if (!node.IsThread)
return node.TimeSpentSelf.ToString("f6") + "ms";
else
return null;
}
}
public string TimeSpentSelfPerCall {
get {
if (!node.IsThread) {
double value = node.TimeSpent - node.Children.Aggregate(0.0, (sum, item) => sum + item.TimeSpent);
return (value / node.CallCount).ToString("f6") + "ms";
} else
if (!node.IsThread)
return (node.TimeSpentSelf / node.CallCount).ToString("f6") + "ms";
else
return null;
}
}
@ -369,7 +367,7 @@ namespace ICSharpCode.Profiler.Controls @@ -369,7 +367,7 @@ namespace ICSharpCode.Profiler.Controls
public Visibility CheckBoxVisibility
{
get {
if (this.Children.Count > 0)
if (node.HasChildren)
return Visibility.Visible;
else
return Visibility.Hidden;

Loading…
Cancel
Save