/// <summary> /// Resets all frame statistics. Run exactly once per frame. /// </summary> public void NewFrame() { // Reset the counters we keep track of for (int i = 0; i < ActiveCounters.Length; ++i) { if (ActiveCounters[i]) { long count = FrameStatistics.COUNTERS[i]; var type = (StatisticsCounterType)i; if (!globalStatistics.TryGetValue(type, out var global)) { globalStatistics[type] = global = GlobalStatistics.Get <long>(threadName, type.ToString()); } global.Value = count; currentFrame.Counts[type] = count; currentFrame.FramesPerSecond = Clock.FramesPerSecond; FrameStatistics.COUNTERS[i] = 0; } } if (PendingFrames.Count < max_pending_frames - 1) { PendingFrames.Enqueue(currentFrame); currentFrame = FramesPool.Get(); } currentFrame.Clear(); if (HandleGC) { for (int i = 0; i < lastAmountGarbageCollects.Length; ++i) { int amountCollections = GC.CollectionCount(i); if (lastAmountGarbageCollects[i] != amountCollections) { lastAmountGarbageCollects[i] = amountCollections; currentFrame.GarbageCollections.Add(i); } } } double dampRate = Math.Max(Clock.ElapsedFrameTime, 0) / 1000; averageFrameTime = Interpolation.Damp(averageFrameTime, Clock.ElapsedFrameTime, 0.01, dampRate); //check for dropped (stutter) frames traceCollector?.NewFrame(Clock.ElapsedFrameTime, Math.Max(10, Math.Max(1000 / Clock.MaximumUpdateHz, averageFrameTime) * 4)); consumeStopwatchElapsedTime(); }
public static void NewFrame() { if (Active != active) { active = Active; lastReport = 0; framesSinceLastReport = 0; GlobalStatistics.Clear(group_name); collected_times.Clear(); } if (!active) { return; } //reset frame totals current_collection_type_stack.Clear(); consumeStopwatchElapsedTime(); if (framesSinceLastReport > 0 && clock.CurrentTime - lastReport > 1000) { GlobalStatistics.Clear(group_name); int i = 0; foreach (var t in collected_times.OrderByDescending(t => t.Value.TotalMilliseconds).Take(5)) { string runCount = t.Value.Count / framesSinceLastReport > 1 ? $" ({t.Value.Count / framesSinceLastReport} instances)" : string.Empty; GlobalStatistics.Get <string>(group_name, $"{++i}. {t.Key.Name}{runCount}").Value = $"{t.Value.TotalMilliseconds / framesSinceLastReport:N1}ms"; } collected_times.Clear(); framesSinceLastReport = 0; lastReport = clock.CurrentTime; } framesSinceLastReport++; }
protected override void OnEventWritten(EventWrittenEventArgs data) { switch ((EventType)data.EventId) { case EventType.GCStart_V1 when data.Payload != null: // https://docs.microsoft.com/en-us/dotnet/framework/performance/garbage-collection-etw-events#gcstart_v1_event GlobalStatistics.Get <int>(statistics_grouping, $"Collections Gen{data.Payload[1]}").Value++; break; case EventType.GCHeapStats_V1 when data.Payload != null: // https://docs.microsoft.com/en-us/dotnet/framework/performance/garbage-collection-etw-events#gcheapstats_v1_event for (int i = 0; i <= 6; i += 2) { addStatistic <ulong>($"Size Gen{i / 2}", data.Payload[i]); } addStatistic <ulong>("Finalization queue length", data.Payload[9]); addStatistic <uint>("Pinned objects", data.Payload[10]); break; } }
private void addStatistic <T>(string name, object data) => GlobalStatistics.Get <T>(statistics_grouping, name).Value = (T)data;
protected override void OnEventWritten(EventWrittenEventArgs data) { switch (data.EventSource.Name) { case source_arraypool: switch ((ArrayPoolEventType)data.EventId) { case ArrayPoolEventType.BufferAllocated: statAllocated.Value++; break; case ArrayPoolEventType.BufferRented: statRented.Value++; statInflight.Value++; break; case ArrayPoolEventType.BufferReturned: statReturned.Value++; // the listener may have been started while buffers were already rented. if (statInflight.Value > 0) { statInflight.Value--; } break; } break; case source_runtime: switch ((GCEventType)data.EventId) { case GCEventType.GCStart_V1 when data.Payload != null: // https://docs.microsoft.com/en-us/dotnet/framework/performance/garbage-collection-etw-events#gcstart_v1_event GlobalStatistics.Get <int>(gc_statistics_grouping, $"Collections Gen{data.Payload[1]}").Value++; break; case GCEventType.GCHeapStats_V1 when data.Payload != null: // https://docs.microsoft.com/en-us/dotnet/framework/performance/garbage-collection-etw-events#gcheapstats_v1_event for (int i = 0; i <= 6; i += 2) { addStatistic <ulong>($"Size Gen{i / 2}", data.Payload[i]); } addStatistic <ulong>("Finalization queue length", data.Payload[9]); addStatistic <uint>("Pinned objects", data.Payload[10]); break; case GCEventType.GCAllocationTick_V2 when data.Payload != null: string name = (string)data.Payload[5]; if (string.IsNullOrEmpty(name)) { break; } var allocType = Type.GetType(name, false, false); if (allocType == null) { break; } var finalizeMethod = allocType.GetMethod("Finalize", BindingFlags.NonPublic | BindingFlags.Instance); Debug.Assert(finalizeMethod != null); // All objects have this. if (finalizeMethod.DeclaringType != typeof(object)) { Logger.Log($"Allocated finalizable object: {name}", LoggingTarget.Performance); } break; case GCEventType.FinalizeObject when data.Payload != null: if (data.Payload[0] == null) { break; } var type = getTypeFromHandle((IntPtr)data.Payload[0]); if (type == null) { break; } Logger.Log($"Finalizing object: {type.ReadableName()}", LoggingTarget.Performance); break; } break; } }