DTO containing data we collect from AI. Modified in real time.
This is performance-critical DTO that needs to be quickly accessed in a thread-safe manner.
        public QuickPulseDataSample(QuickPulseDataAccumulator accumulator, IDictionary <string, Tuple <PerformanceCounterData, double> > perfData)
        {
            if (accumulator == null)
            {
                throw new ArgumentNullException(nameof(accumulator));
            }

            if (perfData == null)
            {
                throw new ArgumentNullException(nameof(perfData));
            }

            if (accumulator.StartTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.StartTimestamp));
            }

            if (accumulator.EndTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.EndTimestamp));
            }

            this.StartTimestamp = accumulator.StartTimestamp.Value;
            this.EndTimestamp   = accumulator.EndTimestamp.Value;

            if ((this.EndTimestamp - this.StartTimestamp) < TimeSpan.Zero)
            {
                throw new InvalidOperationException("StartTimestamp must be less than EndTimestamp.");
            }

            TimeSpan sampleDuration = this.EndTimestamp - this.StartTimestamp;

            Tuple <long, long> requestCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIRequestCountAndDurationInTicks);
            long requestCount           = requestCountAndDuration.Item1;
            long requestDurationInTicks = requestCountAndDuration.Item2;

            this.AIRequests                   = (int)requestCount;
            this.AIRequestsPerSecond          = sampleDuration.TotalSeconds > 0 ? requestCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestDurationAveInMs     = requestCount > 0 ? (double)requestDurationInTicks / TimeSpan.TicksPerMillisecond / requestCount : 0;
            this.AIRequestsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestSuccessCount / sampleDuration.TotalSeconds : 0;

            Tuple <long, long> dependencyCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIDependencyCallCountAndDurationInTicks);
            long dependencyCount           = dependencyCountAndDuration.Item1;
            long dependencyDurationInTicks = dependencyCountAndDuration.Item2;

            this.AIDependencyCalls                   = (int)dependencyCount;
            this.AIDependencyCallsPerSecond          = sampleDuration.TotalSeconds > 0 ? dependencyCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallDurationAveInMs     = dependencyCount > 0 ? (double)dependencyDurationInTicks / TimeSpan.TicksPerMillisecond / dependencyCount : 0;
            this.AIDependencyCallsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallSuccessCount / sampleDuration.TotalSeconds : 0;

            this.AIExceptionsPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIExceptionCount / sampleDuration.TotalSeconds : 0;

            this.PerfCountersLookup = perfData.ToDictionary(
                p => (QuickPulseDefaults.CounterOriginalStringMapping.ContainsKey(p.Value.Item1.OriginalString) ? QuickPulseDefaults.CounterOriginalStringMapping[p.Value.Item1.OriginalString] : p.Value.Item1.OriginalString),
                p => p.Value.Item2);

            this.TelemetryDocuments = accumulator.TelemetryDocuments.ToArray();
        }
Exemple #2
0
        public QuickPulseDataAccumulatorManager(CollectionConfiguration collectionConfiguration)
        {
            if (collectionConfiguration == null)
            {
                throw new ArgumentNullException(nameof(collectionConfiguration));
            }

            this.currentDataAccumulator = new QuickPulseDataAccumulator(collectionConfiguration);
        }
        public void QuickPulseDataSampleCalculatesAIDependencyCallsFailedPerSecondCorrectly()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
                                  {
                                      StartTimestamp = DateTimeOffset.UtcNow,
                                      EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2),
                                      AIDependencyCallFailureCount = 10
                                  };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(10.0 / 2, dataSample.AIDependencyCallsFailedPerSecond);
        }
        public void QuickPulseDataSampleCalculatesAIDependencyCallDurationAveWhenDependencyCallCountIsZeroCorrectly()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
                                  {
                                      StartTimestamp = DateTimeOffset.UtcNow,
                                      EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2),
                                      AIDependencyCallCountAndDurationInTicks =
                                          QuickPulseDataAccumulator.EncodeCountAndDuration(0, TimeSpan.FromSeconds(5).Ticks)
                                  };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(0.0, dataSample.AIDependencyCallDurationAveInMs);
        }
        public void QuickPulseDataAccumulatorCollectsTelemetryItemsInThreadSafeManner()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator();

            // ACT
            var iterationCount = 1000;
            var concurrency = 12;

            Action addItemTask =
                () =>
                Enumerable.Range(0, iterationCount)
                    .ToList()
                    .ForEach(
                        i => accumulator.TelemetryDocuments.Push(new RequestTelemetryDocument()
                                                                     {
                                                                         Name = i.ToString(CultureInfo.InvariantCulture)
                                                                     }));

            var tasks = new List<Action>();
            for (int i = 0; i < concurrency; i++)
            {
                tasks.Add(addItemTask);
            }

            Parallel.Invoke(new ParallelOptions() { MaxDegreeOfParallelism = concurrency }, tasks.ToArray());

            // ASSERT
            var dict = new Dictionary<int, int>();
            foreach (var item in accumulator.TelemetryDocuments)
            {
                int requestNumber = int.Parse(((RequestTelemetryDocument)item).Name, CultureInfo.InvariantCulture);
                if (dict.ContainsKey(requestNumber))
                {
                    dict[requestNumber]++;
                }
                else
                {
                    dict[requestNumber] = 1;
                }
            }

            Assert.AreEqual(iterationCount, dict.Count);
            Assert.IsTrue(dict.All(pair => pair.Value == concurrency));
        }
        public QuickPulseDataAccumulator CompleteCurrentDataAccumulator()
        {
            /*
                Here we need to
                    - promote currentDataAccumulator to completedDataAccumulator
                    - reset (zero out) the new currentDataAccumulator

                Certain telemetry items will be "sprayed" between two neighboring accumulators due to the fact that the snap might occure in the middle of a reader executing its Interlocked's.
            */

            this.completedDataAccumulator = Interlocked.Exchange(ref this.currentDataAccumulator, new QuickPulseDataAccumulator());

            var timestamp = DateTimeOffset.UtcNow;
            this.completedDataAccumulator.EndTimestamp = timestamp;
            this.currentDataAccumulator.StartTimestamp = timestamp;

            return this.completedDataAccumulator;
        }
Exemple #7
0
        public QuickPulseDataAccumulator CompleteCurrentDataAccumulator()
        {
            /*
             *  Here we need to
             *      - promote currentDataAccumulator to completedDataAccumulator
             *      - reset (zero out) the new currentDataAccumulator
             *
             *  Certain telemetry items will be "sprayed" between two neighboring accumulators due to the fact that the snap might occure in the middle of a reader executing its Interlocked's.
             */

            this.completedDataAccumulator = Interlocked.Exchange(ref this.currentDataAccumulator, new QuickPulseDataAccumulator());

            var timestamp = DateTimeOffset.UtcNow;

            this.completedDataAccumulator.EndTimestamp = timestamp;
            this.currentDataAccumulator.StartTimestamp = timestamp;

            return(this.completedDataAccumulator);
        }
Exemple #8
0
        public QuickPulseDataSample(QuickPulseDataAccumulator accumulator, IDictionary <string, Tuple <PerformanceCounterData, double> > perfData, IEnumerable <Tuple <string, int> > topCpuData, bool topCpuDataAccessDenied)
        {
            // NOTE: it is crucial not to keep any heap references on input parameters, new objects with separate roots must be created!
            // CollectionConfiguration is an exception to this rule as it may be still processed, and the sender will check the reference count before sending it out
            if (accumulator == null)
            {
                throw new ArgumentNullException(nameof(accumulator));
            }

            if (perfData == null)
            {
                throw new ArgumentNullException(nameof(perfData));
            }

            if (accumulator.StartTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.StartTimestamp));
            }

            if (accumulator.EndTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.EndTimestamp));
            }

            this.StartTimestamp = accumulator.StartTimestamp.Value;
            this.EndTimestamp   = accumulator.EndTimestamp.Value;

            if ((this.EndTimestamp - this.StartTimestamp) < TimeSpan.Zero)
            {
                throw new InvalidOperationException("StartTimestamp must be less than EndTimestamp.");
            }

            TimeSpan sampleDuration = this.EndTimestamp - this.StartTimestamp;

            Tuple <long, long> requestCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIRequestCountAndDurationInTicks);
            long requestCount           = requestCountAndDuration.Item1;
            long requestDurationInTicks = requestCountAndDuration.Item2;

            this.AIRequests                   = (int)requestCount;
            this.AIRequestsPerSecond          = sampleDuration.TotalSeconds > 0 ? requestCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestDurationAveInMs     = requestCount > 0 ? (double)requestDurationInTicks / TimeSpan.TicksPerMillisecond / requestCount : 0;
            this.AIRequestsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestSuccessCount / sampleDuration.TotalSeconds : 0;

            Tuple <long, long> dependencyCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIDependencyCallCountAndDurationInTicks);
            long dependencyCount           = dependencyCountAndDuration.Item1;
            long dependencyDurationInTicks = dependencyCountAndDuration.Item2;

            this.AIDependencyCalls                   = (int)dependencyCount;
            this.AIDependencyCallsPerSecond          = sampleDuration.TotalSeconds > 0 ? dependencyCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallDurationAveInMs     = dependencyCount > 0 ? (double)dependencyDurationInTicks / TimeSpan.TicksPerMillisecond / dependencyCount : 0;
            this.AIDependencyCallsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallSuccessCount / sampleDuration.TotalSeconds : 0;

            this.AIExceptionsPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIExceptionCount / sampleDuration.TotalSeconds : 0;

            this.GlobalDocumentQuotaReached = accumulator.GlobalDocumentQuotaReached;

            try
            {
                this.PerfCountersLookup =
                    perfData.ToDictionary(
                        p =>
                        (QuickPulseDefaults.DefaultCounterOriginalStringMapping.ContainsKey(p.Value.Item1.ReportAs)
                             ? QuickPulseDefaults.DefaultCounterOriginalStringMapping[p.Value.Item1.ReportAs]
                             : p.Value.Item1.ReportAs),
                        p => p.Value.Item2);
            }
            catch (Exception e)
            {
                // something went wrong with counter names, log an error
                QuickPulseEventSource.Log.UnknownErrorEvent(e.ToString());
            }

            this.TopCpuData = topCpuData.ToArray();

            this.TelemetryDocuments = accumulator.TelemetryDocuments.ToArray();

            this.TopCpuDataAccessDenied = topCpuDataAccessDenied;

            this.CollectionConfigurationAccumulator = accumulator.CollectionConfigurationAccumulator;
        }
        public QuickPulseDataSample(QuickPulseDataAccumulator accumulator, IDictionary<string, Tuple<PerformanceCounterData, float>> perfData)
        {
            if (accumulator == null)
            {
                throw new ArgumentNullException(nameof(accumulator));
            }

            if (perfData == null)
            {
                throw new ArgumentNullException(nameof(perfData));
            }

            if (accumulator.StartTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.StartTimestamp));
            }

            if (accumulator.EndTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.EndTimestamp));
            }

            this.StartTimestamp = accumulator.StartTimestamp.Value;
            this.EndTimestamp = accumulator.EndTimestamp.Value;

            if ((this.EndTimestamp - this.StartTimestamp) < TimeSpan.Zero)
            {
                throw new InvalidOperationException("StartTimestamp must be lesser than EndTimestamp.");
            }

            TimeSpan sampleDuration = this.EndTimestamp - this.StartTimestamp;

            Tuple<long, long> requestCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIRequestCountAndDurationInTicks);
            long requestCount = requestCountAndDuration.Item1;
            long requestDurationInTicks = requestCountAndDuration.Item2;

            this.AIRequests = (int)requestCount;
            this.AIRequestsPerSecond = sampleDuration.TotalSeconds > 0 ? requestCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestDurationAveInMs = requestCount > 0 ? (double)requestDurationInTicks / TimeSpan.TicksPerMillisecond / requestCount : 0;
            this.AIRequestsFailedPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestSuccessCount / sampleDuration.TotalSeconds : 0;

            Tuple<long, long> dependencyCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIDependencyCallCountAndDurationInTicks);
            long dependencyCount = dependencyCountAndDuration.Item1;
            long dependencyDurationInTicks = dependencyCountAndDuration.Item2;

            this.AIDependencyCalls = (int)dependencyCount;
            this.AIDependencyCallsPerSecond = sampleDuration.TotalSeconds > 0 ? dependencyCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallDurationAveInMs = dependencyCount > 0 ? (double)dependencyDurationInTicks / TimeSpan.TicksPerMillisecond / dependencyCount : 0;
            this.AIDependencyCallsFailedPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallSuccessCount / sampleDuration.TotalSeconds : 0;

            this.AIExceptionsPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIExceptionCount / sampleDuration.TotalSeconds : 0;

            // avoiding reflection (Enum.GetNames()) to speed things up
            Tuple<PerformanceCounterData, float> value;

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfIisQueueSize.ToString(), out value))
            {
                this.PerfIisQueueSize = value.Item2;
            }

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfCpuUtilization.ToString(), out value))
            {
                this.PerfCpuUtilization = value.Item2;
            }

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfMemoryInBytes.ToString(), out value))
            {
                this.PerfMemoryInBytes = value.Item2;
            }

            this.PerfCountersLookup = perfData.ToDictionary(p => p.Value.Item1.OriginalString, p => p.Value.Item2);
        }
        public void QuickPulseDataSampleTimestampsItselfCorrectly()
        {
            // ARRANGE
            var timestampStart = DateTimeOffset.UtcNow;
            var timestampEnd = DateTimeOffset.UtcNow.AddSeconds(3);
            var accumulator = new QuickPulseDataAccumulator { StartTimestamp = timestampStart, EndTimestamp = timestampEnd };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(timestampStart, dataSample.StartTimestamp);
            Assert.AreEqual(timestampEnd, dataSample.EndTimestamp);
        }
        public void QuickPulseDataSampleHandlesAbsentCounterInPerfData()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
            {
                StartTimestamp = DateTimeOffset.UtcNow,
                EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2)
            };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.IsFalse(dataSample.PerfCountersLookup.Any());
        }
        public void QuickPulseDataSampleCalculatesAIRpsCorrectly()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
                                  {
                                      StartTimestamp = DateTimeOffset.UtcNow,
                                      EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2),
                                      AIRequestCountAndDurationInTicks = QuickPulseDataAccumulator.EncodeCountAndDuration(10, 0)
                                  };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(10.0 / 2, dataSample.AIRequestsPerSecond);
        }
        public void QuickPulseDataSampleCalculatesAIRequestDurationAveCorrectly()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
                                  {
                                      StartTimestamp = DateTimeOffset.UtcNow,
                                      EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2),
                                      AIRequestCountAndDurationInTicks =
                                          QuickPulseDataAccumulator.EncodeCountAndDuration(10, TimeSpan.FromSeconds(5).Ticks)
                                  };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(TimeSpan.FromSeconds(5).TotalMilliseconds / 10.0, dataSample.AIRequestDurationAveInMs);
        }
        public void QuickPulseDataSampleCalculatesAIExceptionsPerSecondCorrectly()
        {
            // ARRANGE
            var accumulator = new QuickPulseDataAccumulator
            {
                StartTimestamp = DateTimeOffset.UtcNow,
                EndTimestamp = DateTimeOffset.UtcNow.AddSeconds(2),
                AIExceptionCount = 3
            };

            // ACT
            var dataSample = new QuickPulseDataSample(accumulator, this.dummyDictionary);

            // ASSERT
            Assert.AreEqual(3.0 / 2, dataSample.AIExceptionsPerSecond);
        }
 private QuickPulseDataSample CreateDataSample(
     QuickPulseDataAccumulator accumulator,
     IEnumerable<Tuple<PerformanceCounterData, double>> perfData)
 {
     return new QuickPulseDataSample(accumulator, perfData.ToDictionary(tuple => tuple.Item1.ReportAs, tuple => tuple));
 }
Exemple #16
0
        public QuickPulseDataSample(QuickPulseDataAccumulator accumulator, IDictionary <string, Tuple <PerformanceCounterData, float> > perfData)
        {
            if (accumulator == null)
            {
                throw new ArgumentNullException(nameof(accumulator));
            }

            if (perfData == null)
            {
                throw new ArgumentNullException(nameof(perfData));
            }

            if (accumulator.StartTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.StartTimestamp));
            }

            if (accumulator.EndTimestamp == null)
            {
                throw new ArgumentNullException(nameof(accumulator.EndTimestamp));
            }

            this.StartTimestamp = accumulator.StartTimestamp.Value;
            this.EndTimestamp   = accumulator.EndTimestamp.Value;

            if ((this.EndTimestamp - this.StartTimestamp) < TimeSpan.Zero)
            {
                throw new InvalidOperationException("StartTimestamp must be lesser than EndTimestamp.");
            }

            TimeSpan sampleDuration = this.EndTimestamp - this.StartTimestamp;

            Tuple <long, long> requestCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIRequestCountAndDurationInTicks);
            long requestCount           = requestCountAndDuration.Item1;
            long requestDurationInTicks = requestCountAndDuration.Item2;

            this.AIRequests                   = (int)requestCount;
            this.AIRequestsPerSecond          = sampleDuration.TotalSeconds > 0 ? requestCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestDurationAveInMs     = requestCount > 0 ? (double)requestDurationInTicks / TimeSpan.TicksPerMillisecond / requestCount : 0;
            this.AIRequestsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIRequestsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIRequestSuccessCount / sampleDuration.TotalSeconds : 0;

            Tuple <long, long> dependencyCountAndDuration = QuickPulseDataAccumulator.DecodeCountAndDuration(accumulator.AIDependencyCallCountAndDurationInTicks);
            long dependencyCount           = dependencyCountAndDuration.Item1;
            long dependencyDurationInTicks = dependencyCountAndDuration.Item2;

            this.AIDependencyCalls                   = (int)dependencyCount;
            this.AIDependencyCallsPerSecond          = sampleDuration.TotalSeconds > 0 ? dependencyCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallDurationAveInMs     = dependencyCount > 0 ? (double)dependencyDurationInTicks / TimeSpan.TicksPerMillisecond / dependencyCount : 0;
            this.AIDependencyCallsFailedPerSecond    = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallFailureCount / sampleDuration.TotalSeconds : 0;
            this.AIDependencyCallsSucceededPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIDependencyCallSuccessCount / sampleDuration.TotalSeconds : 0;

            this.AIExceptionsPerSecond = sampleDuration.TotalSeconds > 0 ? accumulator.AIExceptionCount / sampleDuration.TotalSeconds : 0;

            // avoiding reflection (Enum.GetNames()) to speed things up
            Tuple <PerformanceCounterData, float> value;

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfIisQueueSize.ToString(), out value))
            {
                this.PerfIisQueueSize = value.Item2;
            }

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfCpuUtilization.ToString(), out value))
            {
                this.PerfCpuUtilization = value.Item2;
            }

            if (perfData.TryGetValue(QuickPulsePerfCounters.PerfMemoryInBytes.ToString(), out value))
            {
                this.PerfMemoryInBytes = value.Item2;
            }

            this.PerfCountersLookup = perfData.ToDictionary(p => p.Value.Item1.OriginalString, p => p.Value.Item2);
        }