4

Is it possible to freeze CPU usage that is shown in in Windows Task Manager? I wish to freeze the load as specific values like 20%, 50%, 70% etc. from my program.

(This is to analyse how much power the PC is consuming with regard to CPU usage.)

Is this possible?

Raj
  • 440
  • 2
  • 6
  • 22

2 Answers2

7

My first naive attempt would be to spawn 2x threads as cores -- each thread in the highest priority and then, within each thread, run a busy-loop and do some work. (More threads than cores is to "steal" all the time I can get from other threads in windows :-)

Using some kind of API to read the CPU load (perhaps WMI or performance counters?) and I would then make each thread 'yield' from the busy loop (sleep for a certain amount of time each loop) until I get the approximate load in the feedback cycle.

This cycle would be self-adjusting: too high load, sleep more. Too low load, sleep less. It's not an exact science, but I think that with some tweaking a stable load can be obtained.

But, I have no idea, really :-)

Happy coding.


Also, consider power management -- sometimes it can lock a CPU at a "max %". Then fully load the CPU and it will max out at that limit. (Windows 7, at least, has a built-in feature to do this, depending upon CPU and chip-set -- there are likely many 3rd party tools.)

The situation becomes rather confusing with newer CPUs that dynamically clocked based on load and temperature, etc.


Here is my attempt at the "naive" approach for .NET 3.5. Make sure to include the System.Management reference.

The CPU utilization as reported by the Task Manager hovers within a few percent of the target -- average seems pretty darn close -- on my system. YMMV, but there is some flexibility for adjustment.

Happy coding (again).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;
using System.Threading;
using System.Diagnostics;

namespace CPULoad
{
    class Program
    {
        // What to try to get :-)
        static int TargetCpuUtilization = 50;
        // An average window too large results in bad harmonics -- keep it small.
        static int AverageWindow = 5;
        // A somewhat large number gets better results here.
        static int ThreadsPerCore = 8;
        // WMI is *very slow* compared to a PerformanceCounter.
        // It still works, but each cycle is *much* longer and it doesn't
        // exhibit as good of characteristics in maintaining a stable load.
        // (It also seems to run a few % higher).
        static bool UseWMI = false;
        // Not sure if this helps -- but just play about :-)
        static bool UseQuestionableAverage = true;

        static int CoreCount () {
            var sys = new ManagementObject("Win32_ComputerSystem.Name=\"" + Environment.MachineName + "\"");
            return int.Parse("" + sys["NumberOfLogicalProcessors"]);
        }

        static Func<int> GetWmiSampler () {
            var searcher = new ManagementObjectSearcher(
                @"root\CIMV2",
                "SELECT PercentProcessorTime FROM Win32_PerfFormattedData_PerfOS_Processor");
            return () => {
                var allCores = searcher.Get().OfType<ManagementObject>().First();
                return int.Parse("" + allCores["PercentProcessorTime"]);
            };
        }

        static Func<int> GetCounterSampler () {
            var cpuCounter = new PerformanceCounter {
                CategoryName = "Processor",
                CounterName = "% Processor Time",
                InstanceName = "_Total",
            };
            return () => {
                return (int)cpuCounter.NextValue();
            };
        }

        static Func<LinkedList<int>, int> StandardAverage () {
            return (samples) => {
                return (int)samples.Average();
            };    
        }

        // Bias towards newest samples
        static Func<LinkedList<int>, int> QuestionableAverage () {
            return (samples) => {
                var weight = 4.0;
                var sum = 0.0;
                var max = 0.0;
                foreach (var sample in samples) {
                    sum += sample * weight;
                    max += weight;
                    weight = Math.Min(4, Math.Max(1, weight * 0.8));
                }
                return (int)(sum / max);
            };
        }

        static void Main (string[] args) {
            var threadCount = CoreCount() * ThreadsPerCore;
            var threads = new List<Thread>();
            for (var i = 0; i < threadCount; i++) {
                Console.WriteLine("Starting thread #" + i);                
                var thread = new Thread(() => {
                    Loader(
                        UseWMI ? GetWmiSampler() : GetCounterSampler(),
                        UseQuestionableAverage ? QuestionableAverage() : StandardAverage());
                });
                thread.IsBackground = true;
                thread.Priority = ThreadPriority.Highest;
                thread.Start();
                threads.Add(thread);
            }
            Console.ReadKey();
            Console.WriteLine("Fin!");
        }

        static void Loader (Func<int> nextSample, Func<LinkedList<int>, int> average) {
            Random r = new Random();
            long cycleCount = 0;
            int cycleLength = 10;
            int sleepDuration = 15;
            int temp = 0;
            var samples = new LinkedList<int>(new[] { 50 });
            long totalSample = 0;

            while (true) {
                cycleCount++;
                var busyLoops = cycleLength * 1000;
                for (int i = 0; i < busyLoops; i++) {
                    // Do some work
                    temp = (int)(temp * Math.PI);
                }
                // Take a break
                Thread.Sleep(sleepDuration);

                {
                    // Add new sample
                    // This seems to work best when *after* the sleep/yield
                    var sample = nextSample();
                    if (samples.Count >= AverageWindow) {
                        samples.RemoveLast();
                    }
                    samples.AddFirst(sample);
                    totalSample += sample;
                }
                var avg = average(samples);
                // should converge to 0
                var conv = Math.Abs(TargetCpuUtilization - (int)(totalSample / cycleCount));

                Console.WriteLine(string.Format("avg:{0:d2} conv:{1:d2} sleep:{2:d2} cycle-length:{3}",
                    avg, conv, sleepDuration, cycleLength));
                // Manipulating both the sleep duration and work duration seems
                // to have the best effect. We don't change both at the same
                // time as that skews one with the other.
                // Favor the cycle-length adjustment.
                if (r.NextDouble() < 0.05) {
                    sleepDuration += (avg < TargetCpuUtilization) ? -1 : 1;
                    // Don't let sleep duration get unbounded upwards or it
                    // can cause badly-oscillating behavior.
                    sleepDuration = (int)Math.Min(24, Math.Max(0, sleepDuration));
                } else {
                    cycleLength += (avg < TargetCpuUtilization) ? 1 : -1;
                    cycleLength = (int)Math.Max(5, cycleLength);
                }
            }
        }
    }
}

While Windows is a preemptive operating system, code which runs in Kernel Mode -- such as drivers -- is preempted by far less. While not doable in C# AFAIK, this should yield a method of stricter load control than the above, but also has a good bit more complexity (and the ability to crash the entire system :-)

There is Process.PriorityClass, but setting this to anything but normal yielded lest consistent behavior for me.

Community
  • 1
  • 1
0

I don't know if you can do that, but you can change the thread priority of the executing thread via the Priority property. You would set that by:

Thread.CurrentThread.Priority = ThreadPriority.Lowest;

Also, I don't think you really want to cap it. If the machine is otherwise idle, you'd like it to get busy on with the task, right? ThreadPriority helps communicate this to the scheduler.

Reference : How to restrict the CPU usage a C# program takes?

Community
  • 1
  • 1
Farzin Zaker
  • 3,578
  • 3
  • 25
  • 35