コード例 #1
0
        protected override object UpdateAggregate_Stage1(MetricValuesBufferBase <double> buffer, int minFlushIndex, int maxFlushIndex)
        {
            Data bufferData = new Data();

            bufferData.Count        = 0;
            bufferData.Min          = Double.MaxValue;
            bufferData.Max          = Double.MinValue;
            bufferData.Sum          = 0.0;
            bufferData.SumOfSquares = 0.0;

            for (int index = minFlushIndex; index <= maxFlushIndex; index++)
            {
                double metricValue = buffer.GetAndResetValue(index);

                if (Double.IsNaN(metricValue))
                {
                    continue;
                }

                bufferData.Count++;
                bufferData.Max           = (metricValue > bufferData.Max) ? metricValue : bufferData.Max;
                bufferData.Min           = (metricValue < bufferData.Min) ? metricValue : bufferData.Min;
                bufferData.Sum          += metricValue;
                bufferData.SumOfSquares += metricValue * metricValue;
            }

            if (this.restrictToUInt32Values)
            {
                bufferData.Max = Math.Round(bufferData.Max);
                bufferData.Min = Math.Round(bufferData.Min);
            }

            return(bufferData);
        }
コード例 #2
0
        protected override object UpdateAggregate_Stage1(MetricValuesBufferBase <double> buffer, int minFlushIndex, int maxFlushIndex)
        {
            // Compute a summary of the buffer:
            Data bufferData = new Data();

            bufferData.HasValues = false;
            bufferData.Min       = Double.MaxValue;
            bufferData.Max       = Double.MinValue;
            bufferData.Last      = Double.NaN;

            for (int index = minFlushIndex; index <= maxFlushIndex; index++)
            {
                double metricValue = buffer.GetAndResetValue(index);

                if (Double.IsNaN(metricValue))
                {
                    continue;
                }

                bufferData.HasValues = true;
                bufferData.Max       = (metricValue > bufferData.Max) ? metricValue : bufferData.Max;
                bufferData.Min       = (metricValue < bufferData.Min) ? metricValue : bufferData.Min;
                bufferData.Last      = metricValue;
            }

            return(bufferData);
        }
コード例 #3
0
        /// <summary>
        /// Flushes the values buffer to update the aggregate state held by subclasses.
        /// </summary>
        /// <param name="buffer"></param>
        private void UpdateAggregate(MetricValuesBufferBase <TBufferedValue> buffer)
        {
            if (buffer == null)
            {
                return;
            }

#if DEBUG
            unchecked
            {
                Interlocked.Increment(ref s_countBufferFlushes);
            }
#endif

            object stage1Result;

            // This lock is only contended is a user called CreateAggregateUnsafe or CompleteAggregation.
            // This is very unlikely to be the case in a tight loop.
            lock (buffer)
            {
                int maxFlushIndex = Math.Min(buffer.PeekLastWriteIndex(), buffer.Capacity - 1);
                int minFlushIndex = buffer.NextFlushIndex;

                if (minFlushIndex > maxFlushIndex)
                {
                    return;
                }

                stage1Result = UpdateAggregate_Stage1(buffer, minFlushIndex, maxFlushIndex);

                buffer.NextFlushIndex = maxFlushIndex + 1;
            }

            UpdateAggregate_Stage2(stage1Result);
        }
        protected override object UpdateAggregate_Stage1(MetricValuesBufferBase <object> buffer, int minFlushIndex, int maxFlushIndex)
        {
            lock (_updateLock)
            {
                for (int index = minFlushIndex; index <= maxFlushIndex; index++)
                {
                    object metricValue = buffer.GetAndResetValue(index);

                    if (metricValue == null)
                    {
                        continue;
                    }

                    string stringValue = metricValue.ToString();

                    if (!_caseSensitive)
                    {
                        stringValue = stringValue.ToLowerInvariant();
                    }

                    _uniqueValues.Add(stringValue);
                    Interlocked.Increment(ref _totalValuesCount);
                }
            }

            return(null);
        }
コード例 #5
0
        protected override object UpdateAggregate_Stage1(MetricValuesBufferBase <double> buffer, int minFlushIndex, int maxFlushIndex)
        {
            lock (_updateLock)
            {
                for (int index = minFlushIndex; index <= maxFlushIndex; index++)
                {
                    double metricValue = buffer.GetAndResetValue(index);

                    if (Double.IsNaN(metricValue))
                    {
                        continue;
                    }

                    _sum += metricValue;
                    _max  = (_sum > _max) ? _sum : _max;
                    _min  = (_sum < _min) ? _sum : _min;
                }

                if (_restrictToUInt32Values)
                {
                    _sum = Math.Round(_sum);
                    _max = Math.Round(_max);
                    _min = Math.Round(_min);
                }
            }

            return(null);
        }
コード例 #6
0
        private MetricValuesBufferBase <TBufferedValue> InvokeMetricValuesBufferFactory()
        {
#if DEBUG
            unchecked
            {
                Interlocked.Increment(ref s_countNewBufferObjectsCreated);
            }
#endif
            MetricValuesBufferBase <TBufferedValue> buffer = _metricValuesBufferFactory();

            if (buffer == null)
            {
                throw new InvalidOperationException($"{nameof(_metricValuesBufferFactory)}-delegate returned null. This is not allowed. Bad aggregator?");
            }

            return(buffer);
        }
コード例 #7
0
        public MetricSeriesAggregatorBase(
            Func <MetricValuesBufferBase <TBufferedValue> > metricValuesBufferFactory,
            IMetricSeriesConfiguration configuration,
            MetricSeries dataSeries,
            MetricAggregationCycleKind aggregationCycleKind)
        {
            Util.ValidateNotNull(metricValuesBufferFactory, nameof(metricValuesBufferFactory));
            Util.ValidateNotNull(configuration, nameof(configuration));

            _dataSeries           = dataSeries;
            _aggregationCycleKind = aggregationCycleKind;
            _isPersistent         = configuration.RequiresPersistentAggregation;

            _metricValuesBufferFactory = metricValuesBufferFactory;
            _metricValuesBuffer        = InvokeMetricValuesBufferFactory();

            Reset(default(DateTimeOffset), default(IMetricValueFilter));
        }
コード例 #8
0
        /// <summary>
        /// This method is the meat of the lock-free aggregation logic.
        /// </summary>
        /// <param name="metricValue">Already filtered and conveted value to be tracked.
        ///     We know that the value is not Double.NaN and not null and it passed trought any filters.</param>
        private void TrackFilteredConvertedValue(TBufferedValue metricValue)
        {
            // Get reference to the current buffer:
            MetricValuesBufferBase <TBufferedValue> buffer = _metricValuesBuffer;

            // Get the index at which to store metricValue into the buffer:
            int index = buffer.IncWriteIndex();

            // Check to see whether we are past the end of the buffer.
            // If we are, it means that some *other* thread hit exactly the end (wrote the last value that fits into the buffer) and is currently flushing.
            // If we are, we will spin and wait.
            if (index >= buffer.Capacity)
            {
#if DEBUG
                int startMillis = Environment.TickCount;
#endif
                var spinWait = new SpinWait();

                // It could be that the thread that was flushing is done and has updated the buffer pointer.
                // We refresh our local reference and see if we now have a valid index into the buffer.
                buffer = _metricValuesBuffer;
                index  = buffer.IncWriteIndex();

                while (index >= buffer.Capacity)
                {
                    // Still not valid index into the buffer. Spin and try again.
                    spinWait.SpinOnce();
#if DEBUG
                    unchecked
                    {
                        Interlocked.Increment(ref s_countBufferWaitSpinCycles);
                    }
#endif
                    // In tests (including stress tests) we always finished wating before 100 cycles. However, this is a protection
                    // against en extreme case on a slow machine. We will back off and sleep for a few millisecs to give th emachine
                    // a chance to finisah current tasks.
                    if (spinWait.Count % 100 == 0)
                    {
                        Task.Delay(10).ConfigureAwait(continueOnCapturedContext: false).GetAwaiter().GetResult();
                    }

                    // Check to see whether the thread that was flushing is done and has updated the buffer pointer.
                    // We refresh our local reference and see if we now have a valid index into the buffer.
                    buffer = _metricValuesBuffer;
                    index  = buffer.IncWriteIndex();
                }
#if DEBUG
                unchecked
                {
                    int periodMillis      = Environment.TickCount - startMillis;
                    int currentSpinMillis = s_timeBufferWaitSpinMillis;
                    int prevSpinMillis    = Interlocked.CompareExchange(ref s_timeBufferWaitSpinMillis, currentSpinMillis + periodMillis, currentSpinMillis);
                    while (prevSpinMillis != currentSpinMillis)
                    {
                        currentSpinMillis = s_timeBufferWaitSpinMillis;
                        prevSpinMillis    = Interlocked.CompareExchange(ref s_timeBufferWaitSpinMillis, currentSpinMillis + periodMillis, currentSpinMillis);
                    }

                    Interlocked.Increment(ref s_countBufferWaitSpinEvents);
                }
#endif
            }

            // Ok, so now we know that (0 <= index = buffer.Capacity). Write the value to the buffer:
            buffer.WriteValue(index, metricValue);

            // If this was the last value that fits into the buffer, we must flush the buffer:
            if (index == buffer.Capacity - 1)
            {
                // Before we begin flushing (which is can take time), we update the _metricValuesBuffer to a fresh buffer that is ready to take values.
                // That way threads do notneed to spin and wait until we flush and can begin writing values.

                // We try to recycle a previous buffer to lower stress on GC and to lower Gen-2 heap fragmentation.
                // The lifetime of an buffer can easily be a minute or so and then it can get into Gen-2 GC heap.
                // If we then, keep throwing such buffers away we can fragment the Gen-2 heap. To avoid this we employ
                // a simple form of best-effort object pooling.

                // Get buffer from pool and reset the pool:
                MetricValuesBufferBase <TBufferedValue> newBufer = Interlocked.Exchange(ref _metricValuesBufferRecycle, null);

                if (newBufer != null)
                {
                    // If we were succesful in getting a recycled buffer from the pool, we will try to use it as the new buffer.
                    // If we successfully the the recycled buffer to be the new buffer, we will reset it to prepare for data.
                    // Otherwise we will just throw it away.

                    MetricValuesBufferBase <TBufferedValue> prevBuffer = Interlocked.CompareExchange(ref _metricValuesBuffer, newBufer, buffer);
                    if (prevBuffer == buffer)
                    {
                        newBufer.ResetIndices();
                    }
                }
                else
                {
                    // If we were succesful in getting a recycled buffer from the pool, we will create a new one.

                    newBufer = InvokeMetricValuesBufferFactory();
                    Interlocked.CompareExchange(ref _metricValuesBuffer, newBufer, buffer);
                }

                // Ok, now we have either set a new buffer that is ready to be used, or we have determined using CompareExchange
                // that another thread set a new buffer and we do not need to do it here.

                // Now we can actually flush the buffer:

                UpdateAggregate(buffer);

                // The buffer is now flushed. If the slot for the best-effor object pooling is free, use it:
                Interlocked.CompareExchange(ref _metricValuesBufferRecycle, buffer, null);
            }
        }
コード例 #9
0
 protected abstract object UpdateAggregate_Stage1(MetricValuesBufferBase <TBufferedValue> buffer, int minFlushIndex, int maxFlushIndex);