jiangok
jiangok

Reputation: 2692

is there a way to make powershell script sleep in nano seconds?

I am using the following PowerShell script to generate events and simulate different system loads. When $sleepIntervalInMilliSeconds=0, CPU usage is 100%.

Then the CPU usage is similar (56%) for any value of $sleepIntervalInMilliSeconds >= 1.

I think the script needs to sleep in the magnitude of nanoseconds instead of milliseconds so that I can control CPU load to be 70%, 80% 90%.

Is there a way to do this? A float number < 1 seems not work. Appreciate any clue.

Param(
  [int]$sleepIntervalInMilliSeconds = 0
)

$eventGenScript = {
  $logName = "TestLog"
  $sourceName = "TestSource"

  while($true) {
    Write-EventLog -LogName $logName -Source $sourceName -Message "perfLog" -EventId 0 -EntryType information
    Start-Sleep -Milliseconds $using:sleepIntervalInMilliSeconds
  }
}

$threadCount = 2

for($i=0; $i -lt $threadCount; $i++)
{
  Start-Job $eventGenScript  
}

read-host "type a key to exit."

Upvotes: 0

Views: 1373

Answers (1)

n0rd
n0rd

Reputation: 12630

I don't consider myself an expert on OS scheduling, but generally it should work like this:

Operating systems with preemptive multitasking model work by giving processes (threads) time slices (or quanta) they can use for executing. It's process' choice then to either consume all the time allocated (leading to 100% CPU consumption) or consume only part of the time slice and return rest to OS (by calling some sort of sleep function). When time slice consumed completely OS takes control and passes it to another thread, so it has chance to run, too. And this processes goes on until you turn your computer off. This is what's called "scheduling". Multi-core CPUs complicate matters a bit, as at any single moment there can be more than one time slice available. Since single thread cannot consume more than 1 time slice at any moment, single-threaded processes cannot produce 100% load on a multi-core CPU (unless you run few of them). Time slice size generally has fixed size that cannot be too short (it would cause a lot of CPU wasted for switching between threads) or too long (as processes would become less responsive). If there is nothing to run, time slice is given to special system process that switches CPU to low power state (System Idle Process in Windows).

Start-Sleep cmdlet is essentially that sleep call I mentioned above, which says to OS: "I give up participating in scheduling for n milliseconds", which means that your process can choose sleep period only at granularity of a time slice. Basically, the only choice you have is how much time slices you want to skip (and again, no guarantees that process would be woken up exactly after that much time).

Since you cannot control sleep time with required granularity you can try controlling amount of time you consume in time slice. First, let's forget about Write-EventLog as it's floods event log with useless messages and do something simpler.

while ($true) {}

endless loop with no body perfectly consumes 1 core of my 4-core CPU, producing 25% CPU load (3 other cores are idling). We can make it a script block and run it in background several times:

$el = { while ($true) {} };
$numCores = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors;
$jobs = 1..$numCores | ForEach-Object { Start-Job -ScriptBlock $el };
Start-Sleep -Seconds 30
$jobs | Remove-Job -Force

We start background jobs that loop endlessly, one job per core, sleep 30 seconds, then kill them. We can produce 100% CPU load on a system with any number of cores. Now time to make it consume a little less CPU. To do that we'll run finite loop inside of our infinite one then sleep a little. Now, length of that finite loop should be picked carefully and will be very system-specific

$loopIterations = 24000;

$el = 
{
    while ($true) 
    {
        for ($i = 0; $i -lt $args[0]; ++$i){}
        Start-Sleep -Milliseconds 1
    } 
};
$numCores = (Get-WmiObject -Class Win32_ComputerSystem).NumberOfLogicalProcessors;
$jobs = 1..$numCores | ForEach-Object { Start-Job -ScriptBlock $el -ArgumentList $loopIterations};
Start-Sleep -Seconds 30
$jobs | Remove-Job -Force

This code consumes 90% CPU on my system. You'd have to tune it on yours so it loads CPU to the level you want.

Upvotes: 2

Related Questions