PageRenderTime 32ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/PerformanceMeasurement.cs

http://measureitdotnet.codeplex.com
C# | 286 lines | 236 code | 22 blank | 28 comment | 28 complexity | 4e1123edf1c9f23d858f393cce03dd63 MD5 | raw file
  1. /* Copyright (c) Microsoft Corporation. All rights reserved. */
  2. /* AUTHOR: Vance Morrison Date : 10/20/2007 */
  3. using System;
  4. using System.Linq;
  5. using System.Collections.Generic;
  6. using System.Text;
  7. using System.IO;
  8. using System.Web; // for HttpUtility.HtmlEncode
  9. using System.Reflection; // for Assembly
  10. using System.Diagnostics; // for Process, DebuggableAttribute
  11. /* files are best browsed in 'outline form'. You outline code with Ctrl-M Ctrl-O. */
  12. /* see code:PerformanceMeasurement.StatsLogger to get started */
  13. namespace PerformanceMeasurement
  14. {
  15. /// <summary>
  16. /// A code:StatsLogger is something that remembers a set of common
  17. /// atrributes (verison of the code, Machine used, NGENed or JITed ...) as
  18. /// well as a set of performance results from mulitple benchmarks.
  19. /// (represented by a code:StatsCollection)
  20. ///
  21. /// The primary value of a StatsLogger is the
  22. /// code:StatsLogger.DisplayHtmlReport which displayes the data in a
  23. /// user-friendly way.
  24. /// </summary>
  25. public class StatsLogger
  26. {
  27. public StatsLogger(StatsCollection dataSet)
  28. {
  29. this.dataSet = dataSet;
  30. attributes = new Dictionary<string, object>();
  31. }
  32. StatsCollection DataSet { get { return dataSet; } }
  33. public object this[string key]
  34. {
  35. get { return attributes[key]; }
  36. set { attributes[key] = value; }
  37. }
  38. public void Add(string name, Stats sample) { dataSet.Add(name, sample); }
  39. public string Category
  40. {
  41. get { return category; }
  42. set { category = value; }
  43. }
  44. public void AddWithCount(string name, int iterationCount, float scale, Samples samples)
  45. {
  46. if (!string.IsNullOrEmpty(category))
  47. name = category + ": " + name;
  48. if (iterationCount != 1 || scale != 1)
  49. {
  50. name += " [";
  51. if (iterationCount != 1)
  52. {
  53. name = name + "count=" + iterationCount.ToString();
  54. if (scale != 1)
  55. name += " ";
  56. }
  57. if (scale != 1)
  58. name = name + " scale=" + scale.ToString("f1");
  59. name += "]";
  60. }
  61. Add(name, samples);
  62. }
  63. public void DisplayHtmlReport(string reportFileName)
  64. {
  65. TextWriter writer = File.CreateText(reportFileName);
  66. writer.WriteLine("<html>");
  67. writer.WriteLine("<h1> MeasureIt Performance Results </h1>");
  68. object optimizationValue;
  69. if (attributes.TryGetValue("CodeOptimization", out optimizationValue) && ((string)optimizationValue) == "Unoptimized")
  70. {
  71. writer.WriteLine("<font color=red><p>");
  72. writer.WriteLine("Warning: the MeasureIt code was not optimized. The results are likely invalid.");
  73. writer.WriteLine("</p></font>");
  74. }
  75. if (Environment.OSVersion.Version.Major < 6)
  76. {
  77. writer.WriteLine("<b><p>");
  78. writer.WriteLine("Data was collected on a Pre-Vista machine. MeasureIt does NOT automatically");
  79. writer.WriteLine("set the CPU to a high performance power policy. This means the CPU might");
  80. writer.WriteLine("be throttled to save power and can lead to");
  81. writer.WriteLine("incorrect and inconsistant benchmark measurements.");
  82. writer.WriteLine("</p></b>");
  83. }
  84. else if (PowerManagment.CurrentPolicy != PowerManagment.HighPerformance)
  85. {
  86. writer.WriteLine("<font color=red><p>");
  87. writer.WriteLine("Warning: The power policy settings were not set at 'High Performance' during this run.");
  88. writer.WriteLine("This means that the CPU could be throttled to lower frequency resulting in");
  89. writer.WriteLine("incorrect and inconsistant benchmark measurements.");
  90. writer.WriteLine("To correct go to Start Menu -> Contol Panel -> System and Maintance -> Power Options");
  91. writer.WriteLine("and set the power policy to 'High Performance' for the duration of the tests.");
  92. writer.WriteLine("</p></font>");
  93. }
  94. writer.WriteLine("<p>");
  95. writer.WriteLine("Below are the results of running a series of benchmarks. Use the");
  96. writer.WriteLine("<b>MeasureIt /usersGuide</b> for more details on exactly what the benchmarks do.");
  97. writer.WriteLine("</p><p>");
  98. writer.WriteLine("It is very easy for benchmark results to be wrong or misleading. You should read the guidance");
  99. writer.WriteLine("in the <b>MeasureIt /usersGuide</b> before making important decisions based on this data.");
  100. writer.WriteLine("</p><p>");
  101. writer.WriteLine("To improve the stability of the measurements, a may be cloned several times");
  102. writer.WriteLine("and this cloned code is then run in a loop.");
  103. writer.WriteLine("If the benchmark was cloned the 'scale' attribute represents the number of times");
  104. writer.WriteLine("it was cloned, and the count represents the number of times the cloned code was run in a loop");
  105. writer.WriteLine("before the measurement was made. The reported number divides by both");
  106. writer.WriteLine("of these values, so it represents a single instance of the operation being measured.");
  107. writer.WriteLine("</p>");
  108. writer.WriteLine("<p>");
  109. writer.WriteLine("The benchmarks data can vary from run to run, so the benchmark is run several times and");
  110. writer.WriteLine("the statistics are displayed. If we assume a normal distribution, you can expect 68% of all measureuments");
  111. writer.WriteLine("to fall within 1 StdDev of the Mean. You can expect over 95% of all measurements");
  112. writer.WriteLine("to fall witin 2 StdDev of the Mean. Thus 2 StdDev is a good error bound.");
  113. writer.WriteLine("Keep in mind, however that it is not uncommon for the statistics to be quite stable");
  114. writer.WriteLine("during a run and yet very widely across different runs. See the users guide for more.");
  115. writer.WriteLine("</p>");
  116. writer.WriteLine("<p>");
  117. writer.WriteLine("Generally the mean is a better measurment if you use the number to compute an");
  118. writer.WriteLine("aggregate throughput for a large number of items. The median is a better");
  119. writer.WriteLine("guess if you want to best guess of a typical sample. The median is also");
  120. writer.WriteLine("more stable if the sample is noisy (eg has outliers).");
  121. writer.WriteLine("</p>");
  122. writer.WriteLine("<h3>Data collected</h3>");
  123. {
  124. writer.WriteLine("<p>");
  125. writer.WriteLine(UnitsDescription);
  126. writer.WriteLine("</p>");
  127. dataSet.WriteReportTable(writer, Scale);
  128. }
  129. writer.WriteLine("<p>");
  130. {
  131. writer.WriteLine("<h2>Attributes of the machine used to collect the data</h2>");
  132. writer.WriteLine("<table border>");
  133. writer.WriteLine("<tr><th>Attribute</th><th>Value</th></tr>");
  134. foreach (string key in attributes.Keys)
  135. {
  136. object valueObj = this[key];
  137. writer.Write("<tr>");
  138. writer.Write("<td>" + HttpUtility.HtmlEncode(key) + "</td>");
  139. string valueStr = HttpUtility.HtmlEncode(valueObj.ToString());
  140. writer.Write("<td>" + valueStr + "</td>");
  141. writer.WriteLine("<tr>");
  142. }
  143. writer.WriteLine("</table>");
  144. }
  145. writer.WriteLine("</p>");
  146. writer.WriteLine("</html>");
  147. writer.Close();
  148. }
  149. static public void LaunchIE(string fileName)
  150. {
  151. Process process = new Process();
  152. process.StartInfo = new ProcessStartInfo(fileName);
  153. process.Start();
  154. }
  155. public float Scale = 1.0F;
  156. public string UnitsDescription = "Scale in usec";
  157. // TODO: probabably does not belong in this class.
  158. public void CaptureCurrentMachineInfo(Assembly assemblyWithCode, bool skipMachineStats)
  159. {
  160. this["Computer Name"] = Environment.MachineName;
  161. if (!skipMachineStats)
  162. {
  163. ComputerSpecs specs = new ComputerSpecs();
  164. this["Number of Processors"] = specs.NumberOfProcessors;
  165. this["Processor Name "] = specs.ProcessorName;
  166. this["Processor Mhz"] = specs.ProcessorClockSpeedMhz;
  167. this["Memory MBytes"] = specs.MemoryMBytes;
  168. this["L1 Cache KBytes"] = specs.L1KBytes;
  169. this["L2 Cache KBytes"] = specs.L2KBytes;
  170. this["Operating System"] = specs.OperatingSystem;
  171. this["Operating System Version"] = specs.OperatingSystemVersion;
  172. this["Stopwatch resolution (nsec)"] = (CodeTimer.ResolutionUsec * 1000.0).ToString("f3");
  173. }
  174. // Are we NGENed or JITTed?
  175. if (IsNGenedCodeLoaded(assemblyWithCode))
  176. this["CompileType"] = "NGEN";
  177. else
  178. this["CompileType"] = "JIT";
  179. // Are we Appdomain Shared, or not?
  180. CaptureCodeSharing();
  181. DebuggableAttribute debugAttribute = (DebuggableAttribute)System.Attribute.GetCustomAttribute(assemblyWithCode, typeof(System.Diagnostics.DebuggableAttribute));
  182. if (debugAttribute != null && debugAttribute.IsJITOptimizerDisabled)
  183. this["CodeOptimization"] = "Unoptimized";
  184. else
  185. this["CodeOptimization"] = "Optimized";
  186. }
  187. private void CaptureCodeSharing()
  188. {
  189. // get a method in this assembly.
  190. var currentMethod = Assembly.GetAssembly(this.GetType()).GetExportedTypes().First(x=>x.GetMethods()!=null).GetMethods().First();
  191. LoaderOptimizationAttribute loaderAttribute = (LoaderOptimizationAttribute)System.Attribute.GetCustomAttribute(currentMethod, typeof(LoaderOptimizationAttribute));
  192. if (loaderAttribute != null && loaderAttribute.Value == LoaderOptimization.MultiDomain)
  193. this["CodeSharing"] = "AppDomainShared";
  194. else
  195. this["CodeSharing"] = "AppDomainSpecific";
  196. }
  197. static bool IsNGenedCodeLoaded(Assembly assembly)
  198. {
  199. // This is a bit of a hack, basically I find the assemblies file name, then
  200. // look for a module loaded called '<filename>.ni.<ext>'. It is not foolproof,
  201. // but it more than good enough for most purposes.
  202. string assemblyFileName = Path.GetFileName(assembly.ManifestModule.FullyQualifiedName);
  203. string nativeImageExt = "ni" + Path.GetExtension(assemblyFileName);
  204. string nativeImageSuffix = @"\" + Path.ChangeExtension(assemblyFileName, nativeImageExt);
  205. System.Diagnostics.Process myProcess = System.Diagnostics.Process.GetCurrentProcess();
  206. foreach (System.Diagnostics.ProcessModule module in myProcess.Modules)
  207. {
  208. if (module.FileName.EndsWith(nativeImageSuffix, StringComparison.OrdinalIgnoreCase))
  209. return true;
  210. }
  211. return false;
  212. }
  213. #region privates
  214. Dictionary<string, object> attributes;
  215. StatsCollection dataSet;
  216. string category;
  217. #endregion
  218. }
  219. /// <summary>
  220. /// StatsCollection represents a collecton of named of samples (class
  221. /// Stats) that have have been given string names. The data can be
  222. /// looked up by name, but is also collection also remembers the order in
  223. /// which the samples were added, and the names can be enumerated in that
  224. /// order.
  225. /// </summary>
  226. public class StatsCollection
  227. {
  228. public StatsCollection()
  229. {
  230. Measurements = new Dictionary<string, Stats>();
  231. order = new List<string>();
  232. }
  233. public Dictionary<string, Stats> Measurements;
  234. public Stats this[string key] { get { return Measurements[key]; } }
  235. public bool ContainsKey(string key) { return Measurements.ContainsKey(key); }
  236. public void Add(string key, Stats value)
  237. {
  238. Measurements.Add(key, value);
  239. order.Add(key);
  240. }
  241. public void WriteReportTable(TextWriter writer, float scale)
  242. {
  243. writer.WriteLine("<table border>");
  244. writer.WriteLine("<tr><th>Name</th><th>Median</th><th>Mean</th><th>StdDev</th><th>Min</th><th>Max</th><th>Samples</th></tr>");
  245. foreach (string key in this.Measurements.Keys)
  246. {
  247. Stats value = this[key];
  248. writer.Write("<tr>");
  249. writer.Write("<td>" + HttpUtility.HtmlEncode(key) + "</td>");
  250. writer.Write("<td>" + (value.Median / scale).ToString("f3") + "</td>");
  251. writer.Write("<td>" + (value.Mean / scale).ToString("f3") + "</td>");
  252. writer.Write("<td>" + (value.StandardDeviation / scale).ToString("f3") + "</td>");
  253. writer.Write("<td>" + (value.Minimum / scale).ToString("f3") + "</td>");
  254. writer.Write("<td>" + (value.Maximum / scale).ToString("f3") + "</td>");
  255. writer.Write("<td>" + value.Count + "</td>");
  256. writer.WriteLine("</tr>");
  257. }
  258. writer.WriteLine("</table>");
  259. }
  260. #region privates
  261. List<string> order;
  262. #endregion
  263. }
  264. }