Beispiel #1
0
        internal bool NotifyWorkItemComplete()
        {
            // TODO: Check perf. Might need to make this thread-local.
            Interlocked.Increment(ref _completionCount);
            Volatile.Write(ref _separated.lastDequeueTime, Environment.TickCount);

            if (ShouldAdjustMaxWorkersActive())
            {
                bool acquiredLock = _hillClimbingThreadAdjustmentLock.TryAcquire();
                try
                {
                    if (acquiredLock)
                    {
                        AdjustMaxWorkersActive();
                    }
                }
                finally
                {
                    if (acquiredLock)
                    {
                        _hillClimbingThreadAdjustmentLock.Release();
                    }
                }
            }

            return(!WorkerThread.ShouldStopProcessingWorkNow());
        }
Beispiel #2
0
        internal bool NotifyWorkItemComplete()
        {
            _completionCounter.Increment();
            Volatile.Write(ref _separated.lastDequeueTime, Environment.TickCount);

            if (ShouldAdjustMaxWorkersActive() && _hillClimbingThreadAdjustmentLock.TryAcquire())
            {
                try
                {
                    AdjustMaxWorkersActive();
                }
                finally
                {
                    _hillClimbingThreadAdjustmentLock.Release();
                }
            }

            return(!WorkerThread.ShouldStopProcessingWorkNow());
        }
Beispiel #3
0
        //
        // This method must only be called if ShouldAdjustMaxWorkersActive has returned true, *and*
        // _hillClimbingThreadAdjustmentLock is held.
        //
        private void AdjustMaxWorkersActive()
        {
            LowLevelLock threadAdjustmentLock = _threadAdjustmentLock;

            if (!threadAdjustmentLock.TryAcquire())
            {
                // The lock is held by someone else, they will take care of this for us
                return;
            }

            bool addWorker = false;

            try
            {
                // Repeated checks from ShouldAdjustMaxWorkersActive() inside the lock
                ThreadCounts counts = _separated.counts;
                if (counts.NumProcessingWork > counts.NumThreadsGoal ||
                    _pendingBlockingAdjustment != PendingBlockingAdjustment.None)
                {
                    return;
                }

                long   endTime        = Stopwatch.GetTimestamp();
                double elapsedSeconds = Stopwatch.GetElapsedTime(_currentSampleStartTime, endTime).TotalSeconds;

                if (elapsedSeconds * 1000 >= _threadAdjustmentIntervalMs / 2)
                {
                    int currentTicks        = Environment.TickCount;
                    int totalNumCompletions = (int)_completionCounter.Count;
                    int numCompletions      = totalNumCompletions - _separated.priorCompletionCount;

                    short oldNumThreadsGoal = counts.NumThreadsGoal;
                    int   newNumThreadsGoal;
                    (newNumThreadsGoal, _threadAdjustmentIntervalMs) =
                        HillClimbing.ThreadPoolHillClimber.Update(oldNumThreadsGoal, elapsedSeconds, numCompletions);
                    if (oldNumThreadsGoal != (short)newNumThreadsGoal)
                    {
                        _separated.counts.InterlockedSetNumThreadsGoal((short)newNumThreadsGoal);

                        //
                        // If we're increasing the goal, inject a thread.  If that thread finds work, it will inject
                        // another thread, etc., until nobody finds work or we reach the new goal.
                        //
                        // If we're reducing the goal, whichever threads notice this first will sleep and timeout themselves.
                        //
                        if (newNumThreadsGoal > oldNumThreadsGoal)
                        {
                            addWorker = true;
                        }
                    }

                    _separated.priorCompletionCount          = totalNumCompletions;
                    _separated.nextCompletedWorkRequestsTime = currentTicks + _threadAdjustmentIntervalMs;
                    Volatile.Write(ref _separated.priorCompletedWorkRequestsTime, currentTicks);
                    _currentSampleStartTime = endTime;
                }
            }
            finally
            {
                threadAdjustmentLock.Release();
            }

            if (addWorker)
            {
                WorkerThread.MaybeAddWorkingWorker(this);
            }
        }
Beispiel #4
0
        //
        // This method must only be called if ShouldAdjustMaxWorkersActive has returned true, *and*
        // _hillClimbingThreadAdjustmentLock is held.
        //
        private void AdjustMaxWorkersActive()
        {
            LowLevelLock hillClimbingThreadAdjustmentLock = _hillClimbingThreadAdjustmentLock;

            if (!hillClimbingThreadAdjustmentLock.TryAcquire())
            {
                // The lock is held by someone else, they will take care of this for us
                return;
            }

            try
            {
                long startTime = _currentSampleStartTime;
                long endTime   = Stopwatch.GetTimestamp();
                long freq      = Stopwatch.Frequency;

                double elapsedSeconds = (double)(endTime - startTime) / freq;

                if (elapsedSeconds * 1000 >= _threadAdjustmentIntervalMs / 2)
                {
                    int currentTicks        = Environment.TickCount;
                    int totalNumCompletions = (int)_completionCounter.Count;
                    int numCompletions      = totalNumCompletions - _separated.priorCompletionCount;

                    ThreadCounts currentCounts = _separated.counts.VolatileRead();
                    int          newMax;
                    (newMax, _threadAdjustmentIntervalMs) = HillClimbing.ThreadPoolHillClimber.Update(currentCounts.NumThreadsGoal, elapsedSeconds, numCompletions);

                    while (newMax != currentCounts.NumThreadsGoal)
                    {
                        ThreadCounts newCounts = currentCounts;
                        newCounts.NumThreadsGoal = (short)newMax;

                        ThreadCounts oldCounts = _separated.counts.InterlockedCompareExchange(newCounts, currentCounts);
                        if (oldCounts == currentCounts)
                        {
                            //
                            // If we're increasing the max, inject a thread.  If that thread finds work, it will inject
                            // another thread, etc., until nobody finds work or we reach the new maximum.
                            //
                            // If we're reducing the max, whichever threads notice this first will sleep and timeout themselves.
                            //
                            if (newMax > oldCounts.NumThreadsGoal)
                            {
                                WorkerThread.MaybeAddWorkingWorker(this);
                            }
                            break;
                        }

                        if (oldCounts.NumThreadsGoal > currentCounts.NumThreadsGoal && oldCounts.NumThreadsGoal >= newMax)
                        {
                            // someone (probably the gate thread) increased the thread count more than
                            // we are about to do.  Don't interfere.
                            break;
                        }

                        currentCounts = oldCounts;
                    }

                    _separated.priorCompletionCount          = totalNumCompletions;
                    _separated.nextCompletedWorkRequestsTime = currentTicks + _threadAdjustmentIntervalMs;
                    Volatile.Write(ref _separated.priorCompletedWorkRequestsTime, currentTicks);
                    _currentSampleStartTime = endTime;
                }
            }
            finally
            {
                hillClimbingThreadAdjustmentLock.Release();
            }
        }
Beispiel #5
0
        //
        // This method must only be called if ShouldAdjustMaxWorkersActive has returned true, *and*
        // _hillClimbingThreadAdjustmentLock is held.
        //
        private void AdjustMaxWorkersActive()
        {
            LowLevelLock threadAdjustmentLock = _threadAdjustmentLock;

            if (!threadAdjustmentLock.TryAcquire())
            {
                // The lock is held by someone else, they will take care of this for us
                return;
            }

            bool addWorker = false;

            try
            {
                // Skip hill climbing when there is a pending blocking adjustment. Hill climbing may otherwise bypass the
                // blocking adjustment heuristics and increase the thread count too quickly.
                if (_pendingBlockingAdjustment != PendingBlockingAdjustment.None)
                {
                    return;
                }

                long startTime = _currentSampleStartTime;
                long endTime   = Stopwatch.GetTimestamp();
                long freq      = Stopwatch.Frequency;

                double elapsedSeconds = (double)(endTime - startTime) / freq;

                if (elapsedSeconds * 1000 >= _threadAdjustmentIntervalMs / 2)
                {
                    int currentTicks        = Environment.TickCount;
                    int totalNumCompletions = (int)_completionCounter.Count;
                    int numCompletions      = totalNumCompletions - _separated.priorCompletionCount;

                    int newNumThreadsGoal;
                    (newNumThreadsGoal, _threadAdjustmentIntervalMs) =
                        HillClimbing.ThreadPoolHillClimber.Update(_separated.numThreadsGoal, elapsedSeconds, numCompletions);
                    short oldNumThreadsGoal = _separated.numThreadsGoal;
                    if (oldNumThreadsGoal != (short)newNumThreadsGoal)
                    {
                        _separated.numThreadsGoal = (short)newNumThreadsGoal;

                        //
                        // If we're increasing the goal, inject a thread.  If that thread finds work, it will inject
                        // another thread, etc., until nobody finds work or we reach the new goal.
                        //
                        // If we're reducing the goal, whichever threads notice this first will sleep and timeout themselves.
                        //
                        if (newNumThreadsGoal > oldNumThreadsGoal)
                        {
                            addWorker = true;
                        }
                    }

                    _separated.priorCompletionCount          = totalNumCompletions;
                    _separated.nextCompletedWorkRequestsTime = currentTicks + _threadAdjustmentIntervalMs;
                    Volatile.Write(ref _separated.priorCompletedWorkRequestsTime, currentTicks);
                    _currentSampleStartTime = endTime;
                }
            }
            finally
            {
                threadAdjustmentLock.Release();
            }

            if (addWorker)
            {
                WorkerThread.MaybeAddWorkingWorker(this);
            }
        }