From a3bed1fbcbcbe19681aa57733f260ef647a33d8e Mon Sep 17 00:00:00 2001 From: Oren Hurvitz Date: Mon, 27 Jul 2015 12:16:21 +0300 Subject: Report "Script Execution Time" as the time spent executing the script in the last 30 seconds. Use a sliding window to calculate this. Notes: - This metric provides a better indication of which scripts are taking up a lot of CPU (and therefore should be optimized). - Previously the execution time was reset to 0 in every new measurement period, causing the reported time to fluctuate for no reason. This has been fixed by using a sliding window. --- .../ScriptEngine/Interfaces/IScriptInstance.cs | 9 ++----- .../ScriptEngine/Shared/Instance/ScriptInstance.cs | 29 +++++++++----------- OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | 31 +++------------------- 3 files changed, 18 insertions(+), 51 deletions(-) (limited to 'OpenSim/Region/ScriptEngine') diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs index fa2ceef..f695eba 100644 --- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs @@ -109,14 +109,9 @@ namespace OpenSim.Region.ScriptEngine.Interfaces DateTime TimeStarted { get; } /// - /// Tick the last measurement period was started. + /// Collects information about how long the script was executed. /// - long MeasurementPeriodTickStart { get; } - - /// - /// Ticks spent executing in the last measurement period. - /// - long MeasurementPeriodExecutionTime { get; } + MetricsCollectorTime ExecutionTime { get; } /// /// Scene part in which this script instance is contained. diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 05dea5d..d8ad62c 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -199,11 +199,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance public DateTime TimeStarted { get; private set; } - public long MeasurementPeriodTickStart { get; private set; } + public MetricsCollectorTime ExecutionTime { get; private set; } - public long MeasurementPeriodExecutionTime { get; private set; } - - public static readonly int MaxMeasurementPeriod = 30 * 1000; // show the *recent* time used by the script, to find currently active scripts + private static readonly int MeasurementWindow = 30 * 1000; // show the *recent* time used by the script, to find currently active scripts private bool m_coopTermination; @@ -246,6 +244,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance m_SaveState = StatePersistedHere; + ExecutionTime = new MetricsCollectorTime(MeasurementWindow, 10); + // m_log.DebugFormat( // "[SCRIPT INSTANCE]: Instantiated script instance {0} (id {1}) in part {2} (id {3}) in object {4} attached avatar {5} in {6}", // ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, m_AttachedAvatar, Engine.World.Name); @@ -505,8 +505,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance Running = true; TimeStarted = DateTime.Now; - MeasurementPeriodTickStart = Util.EnvironmentTickCount(); - MeasurementPeriodExecutionTime = 0; + + // Note: we don't reset ExecutionTime. The reason is that runaway scripts are stopped and restarted + // automatically, and we *do* want to show that they had high CPU in that case. If we had reset + // ExecutionTime here then runaway scripts, paradoxically, would never show up in the "Top Scripts" dialog. if (EventQueue.Count > 0) { @@ -832,20 +834,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance m_EventStart = DateTime.Now; m_InEvent = true; - // Reset the measurement period when we reach the end of the current one. - if (Util.EnvironmentTickCountSubtract((int)MeasurementPeriodTickStart) > MaxMeasurementPeriod) - { - MeasurementPeriodTickStart = Util.EnvironmentTickCount(); - MeasurementPeriodExecutionTime = 0; - } + Stopwatch timer = new Stopwatch(); + timer.Start(); - Stopwatch executionTime = new Stopwatch(); - executionTime.Start(); - m_Script.ExecuteEvent(State, data.EventName, data.Params); - executionTime.Stop(); - MeasurementPeriodExecutionTime += executionTime.ElapsedMilliseconds; + timer.Stop(); + ExecutionTime.AddSample(timer); m_InEvent = false; m_CurrentEvent = String.Empty; diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs index ae02877..5071884 100755 --- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -2343,7 +2343,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine public Dictionary GetObjectScriptsExecutionTimes() { - long tickNow = Util.EnvironmentTickCount(); Dictionary topScripts = new Dictionary(); lock (m_Scripts) @@ -2353,7 +2352,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine if (!topScripts.ContainsKey(si.LocalID)) topScripts[si.RootLocalID] = 0; - topScripts[si.RootLocalID] += CalculateAdjustedExectionTime(si, tickNow); + topScripts[si.RootLocalID] += GetExectionTime(si); } } @@ -2367,7 +2366,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine return 0.0f; } float time = 0.0f; - long tickNow = Util.EnvironmentTickCount(); IScriptInstance si; // Calculate the time for all scripts that this engine is executing // Ignore any others @@ -2376,36 +2374,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine si = GetInstance(id); if (si != null && si.Running) { - time += CalculateAdjustedExectionTime(si, tickNow); + time += GetExectionTime(si); } } return time; } - private float CalculateAdjustedExectionTime(IScriptInstance si, long tickNow) + private float GetExectionTime(IScriptInstance si) { - long ticksElapsed = Util.EnvironmentTickCountSubtract((int)tickNow, (int)si.MeasurementPeriodTickStart); - - // Avoid divide by zero - if (ticksElapsed == 0) - ticksElapsed = 1; - - // Scale execution time to the ideal 55 fps frame time for these reasons. - // - // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no - // 'script execution time per frame', which is the original purpose of this value. - // - // 2) Giving the raw execution times is misleading since scripts start at different times, making - // it impossible to compare scripts. - // - // 3) Scaling the raw execution time to the time that the script has been running is better but - // is still misleading since a script that has just been rezzed may appear to have been running - // for much longer. - // - // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect - // since the figure does not represent actual execution time and very hard running scripts will - // never exceed 18ms (though this is a very high number for script execution so is a warning sign). - return ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f; + return (float)si.ExecutionTime.GetSumTime().TotalMilliseconds; } public void SuspendScript(UUID itemID) -- cgit v1.1