| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.Linq; |
| | 4 | | using ReFlex.Core.Common.Interfaces; |
| | 5 | | using ReFlex.Core.Common.Util; |
| | 6 | |
|
| | 7 | | namespace ReFlex.Core.Common.Components |
| | 8 | | { |
| | 9 | |
|
| | 10 | | public class PerformanceAggregator : IPerformanceAggregator, IDisposable |
| | 11 | | { |
| | 12 | | /// <summary> |
| | 13 | | /// Maximum number of performance records to keep |
| | 14 | | /// </summary> |
| | 15 | | private const int MaxPerformanceRecords = 10; |
| | 16 | |
|
| | 17 | | [Obsolete] |
| | 18 | | private const int MaxNumIncompleteRecords = 3; |
| | 19 | |
|
| | 20 | | /// <summary> |
| | 21 | | /// id of the current frame |
| | 22 | | /// </summary> |
| | 23 | | private int _frameId; |
| | 24 | |
|
| | 25 | | /// <summary> |
| | 26 | | /// List of performance reporters registered with the aggregator |
| | 27 | | /// </summary> |
| 8 | 28 | | private readonly List<IPerformanceReporter> _reporters = new List<IPerformanceReporter>(); |
| | 29 | |
|
| | 30 | | /// <summary> |
| | 31 | | /// Container for storing performance data |
| | 32 | | /// </summary> |
| 8 | 33 | | private readonly PerformanceData _performanceData = new PerformanceData |
| 8 | 34 | | { |
| 8 | 35 | | Data = new List<PerformanceDataItem>() |
| 8 | 36 | | }; |
| | 37 | |
|
| | 38 | | /// <summary> |
| | 39 | | /// Flag to determine whether to measure performance or not |
| | 40 | | /// </summary> |
| | 41 | | private bool _measurePerformance; |
| | 42 | |
|
| | 43 | | /// <summary> |
| | 44 | | /// Flag to determine whether to measure performance or not |
| | 45 | | /// Value is propagated to registered reporters, so they start/stop measuring if this value changes |
| | 46 | | /// </summary> |
| | 47 | | public bool MeasurePerformance |
| | 48 | | { |
| 12 | 49 | | get => _measurePerformance; |
| | 50 | | set |
| 5 | 51 | | { |
| 5 | 52 | | _measurePerformance = value; |
| 16 | 53 | | _reporters.ForEach(reporter => reporter.MeasurePerformance = value); |
| 5 | 54 | | } |
| | 55 | | } |
| | 56 | |
|
| | 57 | | /// <summary> |
| | 58 | | /// Event triggered when performance data is updated for propagating current performance data |
| | 59 | | /// </summary> |
| | 60 | | public event EventHandler<PerformanceData> PerformanceDataUpdated; |
| | 61 | |
|
| | 62 | | /// <summary> |
| | 63 | | /// Default constructor |
| | 64 | | /// </summary> |
| 6 | 65 | | public PerformanceAggregator() |
| 6 | 66 | | { |
| 6 | 67 | | } |
| | 68 | |
|
| | 69 | | /// <summary> |
| | 70 | | /// Constructor with a list of reporters to register during construction |
| | 71 | | /// </summary> |
| | 72 | | /// <param name="reporters">Reporters that are to be registered when constructor is executed</param> |
| 2 | 73 | | public PerformanceAggregator(List<IPerformanceReporter> reporters) |
| 2 | 74 | | { |
| 14 | 75 | | foreach (var performanceReporter in reporters) |
| 4 | 76 | | { |
| 4 | 77 | | RegisterReporter(performanceReporter); |
| 4 | 78 | | } |
| 2 | 79 | | } |
| | 80 | |
|
| | 81 | | /// <summary> |
| | 82 | | /// Register a performance reporter with the aggregator. |
| | 83 | | /// When registering, the Aggregator subscribes to the <see cref="PerformanceDataUpdated"/> event of the reporte |
| | 84 | | /// The frame id is initially synchronized between reporter and aggregator and the <see cref="MeasurePerformance |
| | 85 | | /// </summary> |
| | 86 | | /// <param name="reporter">The Performance Reporter that wil be registered</param> |
| | 87 | | public void RegisterReporter(IPerformanceReporter reporter) |
| 13 | 88 | | { |
| 13 | 89 | | if (reporter == null) |
| 1 | 90 | | return; |
| 12 | 91 | | _reporters.Add(reporter); |
| 12 | 92 | | reporter.PerformanceDataUpdated += AddData; |
| 12 | 93 | | SyncFrameId(); |
| 12 | 94 | | reporter.MeasurePerformance = MeasurePerformance; |
| 13 | 95 | | } |
| | 96 | |
|
| | 97 | | /// <summary> |
| | 98 | | /// Unregister a performance reporter from the aggregator. |
| | 99 | | /// Removes reporter from the internal list and unsubscribes from the <see cref="PerformanceDataUpdated"/> event |
| | 100 | | /// </summary> |
| | 101 | | /// <param name="reporter">The reporter which is to be removed from the current list of reporters.</param> |
| | 102 | | public void UnregisterReporter(IPerformanceReporter reporter) |
| 3 | 103 | | { |
| 3 | 104 | | if (reporter == null) |
| 1 | 105 | | return; |
| 2 | 106 | | _reporters.Remove(reporter); |
| 2 | 107 | | reporter.PerformanceDataUpdated -= AddData; |
| 3 | 108 | | } |
| | 109 | |
|
| | 110 | | /// <summary> |
| | 111 | | /// Event handler to add performance data sent by a reporter. |
| | 112 | | /// When data is flagged with <see cref="PerformanceDataStage.Start"/>, the filter data is added and the stage i |
| | 113 | | /// When associated stage is <see cref="PerformanceDataStage.ProcessingData"/>, processing data is merged with e |
| | 114 | | /// Old records are removed with respect to the specification of <see cref="MaxPerformanceRecords"/> |
| | 115 | | /// Records with the stage of <see cref="PerformanceDataStage.Complete"/> are propagated using the <see cref="Pe |
| | 116 | | /// </summary> |
| | 117 | | /// <param name="sender">The instance issuing the event</param> |
| | 118 | | /// <param name="data">the updated Performance data record</param> |
| | 119 | | private void AddData(object sender, PerformanceDataItem data) |
| 27 | 120 | | { |
| 27 | 121 | | var item = CreateNewDataItem(); |
| 27 | 122 | | lock (_performanceData) |
| 27 | 123 | | { |
| 27 | 124 | | var existingProcessData = _performanceData.Data.FirstOrDefault(elem => |
| 187 | 125 | | (elem.Stage == PerformanceDataStage.ProcessingData || elem.Stage == PerformanceD |
| | 126 | |
|
| 27 | 127 | | var existingFilterData = _performanceData.Data.FirstOrDefault(elem => |
| 187 | 128 | | elem.Stage == PerformanceDataStage.FilterDataStored && elem.FrameId == data.FrameId); |
| | 129 | |
|
| 27 | 130 | | if (data.Stage == PerformanceDataStage.Start) |
| 23 | 131 | | { |
| 23 | 132 | | item.Stage = PerformanceDataStage.FilterDataStored; |
| | 133 | |
|
| 23 | 134 | | if (existingFilterData != null) |
| 20 | 135 | | { |
| | 136 | | // increment frame id: incomplete sample - subsequent filter samples |
| 20 | 137 | | _frameId++; |
| 20 | 138 | | existingFilterData.Stage = PerformanceDataStage.Complete; |
| 20 | 139 | | item.FrameId = _frameId; |
| 20 | 140 | | } |
| 3 | 141 | | else if (existingProcessData != null) |
| 1 | 142 | | { |
| 1 | 143 | | item = existingProcessData; |
| 1 | 144 | | item.Stage = PerformanceDataStage.Complete; |
| 1 | 145 | | _performanceData.Data.Remove(existingProcessData); |
| | 146 | |
|
| | 147 | | // increment frame id: data complete (filter + process data) |
| 1 | 148 | | _frameId++; |
| 1 | 149 | | } |
| | 150 | |
|
| 23 | 151 | | item.Filter = data.Filter; |
| 23 | 152 | | _performanceData.Data.Add(item); |
| | 153 | |
|
| 23 | 154 | | } |
| 4 | 155 | | else if (data.Stage == PerformanceDataStage.ProcessingData) |
| 4 | 156 | | { |
| 4 | 157 | | if (existingProcessData != null) |
| 1 | 158 | | { |
| | 159 | | // increment frame id: incomplete sample - subsequent process samples |
| 1 | 160 | | _frameId++; |
| 1 | 161 | | existingProcessData.Stage = PerformanceDataStage.Complete; |
| 1 | 162 | | item.FrameId = _frameId; |
| 1 | 163 | | } |
| 3 | 164 | | else if (existingFilterData != null) |
| 1 | 165 | | { |
| 1 | 166 | | item = existingFilterData; |
| 1 | 167 | | item.Stage = PerformanceDataStage.Complete; |
| 1 | 168 | | _performanceData.Data.Remove(existingFilterData); |
| | 169 | |
|
| | 170 | | // increment frame id: data complete (filter + process data) |
| 1 | 171 | | _frameId++; |
| 1 | 172 | | } |
| | 173 | |
|
| 4 | 174 | | item.Process = data.Process; |
| 4 | 175 | | _performanceData.Data.Add(item); |
| 4 | 176 | | } |
| | 177 | |
|
| 27 | 178 | | SyncFrameId(); |
| | 179 | |
|
| 27 | 180 | | CleanupRecords(); |
| | 181 | |
|
| 27 | 182 | | } |
| | 183 | |
|
| 202 | 184 | | var completed = _performanceData.Data.Where(elem => elem.Stage == PerformanceDataStage.Complete).ToList(); |
| 27 | 185 | | PerformanceDataUpdated?.Invoke(this, new PerformanceData |
| 27 | 186 | | { |
| 27 | 187 | | Data = completed |
| 27 | 188 | | }); |
| 27 | 189 | | } |
| | 190 | |
|
| | 191 | | /// <summary> |
| | 192 | | /// Set local frame Id for all registered reporters |
| | 193 | | /// </summary> |
| | 194 | | private void SyncFrameId() |
| 39 | 195 | | { |
| 86 | 196 | | _reporters.ForEach(reporter => reporter.UpdateFrameId(_frameId)); |
| 39 | 197 | | } |
| | 198 | |
|
| | 199 | | /// <summary> |
| | 200 | | /// Create a new <see cref="PerformanceDataItem"/> with the current frame Id, setting the <see cref="Performanc |
| | 201 | | /// </summary> |
| | 202 | | /// <returns>the created <see cref="PerformanceDataItem"/></returns> |
| | 203 | | private PerformanceDataItem CreateNewDataItem() |
| 27 | 204 | | { |
| 27 | 205 | | var result = new PerformanceDataItem |
| 27 | 206 | | { |
| 27 | 207 | | FrameId = _frameId, |
| 27 | 208 | | FrameStart = DateTime.Now.Ticks, |
| 27 | 209 | | Stage = PerformanceDataStage.Incomplete |
| 27 | 210 | | }; |
| | 211 | |
|
| 27 | 212 | | return result; |
| 27 | 213 | | } |
| | 214 | |
|
| | 215 | | /// <summary> |
| | 216 | | /// Remove all records except the first <see cref="MaxNumIncompleteRecords"/> elements. |
| | 217 | | /// Set all items with the stage <see cref="PerformanceDataStage.Incomplete"/> to <see cref="PerformanceDataStag |
| | 218 | | /// </summary> |
| | 219 | | private void CleanupRecords() |
| 27 | 220 | | { |
| 27 | 221 | | if (_performanceData.Data.Count > MaxPerformanceRecords) |
| 10 | 222 | | _performanceData.Data.RemoveRange(0, _performanceData.Data.Count - MaxPerformanceRecords); |
| 27 | 223 | | } |
| | 224 | |
|
| | 225 | | /// <summary> |
| | 226 | | /// Dispose method to unregister reporters during disposal. |
| | 227 | | /// </summary> |
| | 228 | | public void Dispose() |
| 1 | 229 | | { |
| 7 | 230 | | foreach (var performanceReporter in _reporters.ToList()) |
| 2 | 231 | | { |
| 2 | 232 | | UnregisterReporter(performanceReporter); |
| 2 | 233 | | } |
| 1 | 234 | | } |
| | 235 | | } |
| | 236 | | } |