如何在给定负载(%CPU利用率)下运行CPU?
是否可以冻结Windows任务管理器中显示的CPU使用率? 我想从我的程序中将负载冻结为20%,50%,70%等特定值。
(这是为了分析PC在CPU使用方面消耗的功率。)
这可能吗?
我的第一个天真尝试是将2x线程生成为核心 – 每个线程都处于最高优先级,然后在每个线程中运行繁忙循环并完成一些工作。 (比核心更多的线程是在Windows中从其他线程获得的所有时间“窃取”:-)
使用某种API来读取CPU负载(可能是WMI或性能计数器?)然后我会从繁忙的循环(每个循环hibernate一段时间)让每个线程’收益’,直到我得到大概的负载反馈周期。
这个循环可以自我调整:负载太高,睡眠更多。 负载太低,睡眠少。 这不是一门精确的科学,但我认为通过一些调整可以获得稳定的负荷。
但是,我不知道,真的:-)
快乐的编码。
另外,考虑电源管理 – 有时它可以将CPU锁定在“最大%”。 然后完全加载CPU,它将达到该限制。 (至少Windows 7具有内置function,取决于CPU和芯片组 – 可能有许多第三方工具。)
对于根据负载和温度等动态计时的新CPU,情况变得相当混乱。
这是我尝试.NET 3.5的“天真”方法。 确保包含System.Management
引用。
任务管理器报告的CPU利用率在目标的几个百分点内徘徊 – 平均值似乎非常接近 – 在我的系统上。 YMMV,但调整有一定的灵活性。
快乐编码(再次)。
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 GetWmiSampler () { var searcher = new ManagementObjectSearcher( @"root\CIMV2", "SELECT PercentProcessorTime FROM Win32_PerfFormattedData_PerfOS_Processor"); return () => { var allCores = searcher.Get().OfType().First(); return int.Parse("" + allCores["PercentProcessorTime"]); }; } static Func GetCounterSampler () { var cpuCounter = new PerformanceCounter { CategoryName = "Processor", CounterName = "% Processor Time", InstanceName = "_Total", }; return () => { return (int)cpuCounter.NextValue(); }; } static Func, int> StandardAverage () { return (samples) => { return (int)samples.Average(); }; } // Bias towards newest samples static Func, 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(); 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 nextSample, Func, int> average) { Random r = new Random(); long cycleCount = 0; int cycleLength = 10; int sleepDuration = 15; int temp = 0; var samples = new LinkedList (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); } } } } }
虽然Windows是一种先发制人的操作系统,但在内核模式下运行的代码 - 例如驱动程序 - 却被抢先一步。 虽然在C#AFAIK中不可行,但这应该产生比上述更严格的负载控制方法,但也有更多的复杂性(以及崩溃整个系统的能力:-)
有Process.PriorityClass
,但将此设置为除了正常之外的任何东西都会产生最小的一致行为。
我不知道你是否可以这样做,但你可以通过Priority属性更改执行线程的线程优先级。 你可以通过以下方式设置:
Thread.CurrentThread.Priority = ThreadPriority.Lowest;
另外,我认为你真的不想限制它。 如果机器处于空闲状态,你会希望它忙于完成任务,对吗? ThreadPriority有助于将其传达给调度程序。
参考: 如何限制C#程序的CPU使用率?