I was recently asked:
For testing, I need to generate artificial CPU load on the server, outside of SQL Server. Do you have any ideas? Maybe something with PowerShell?
Heck yea, PowerShell can do that.
I mean, I knew PowerShell could do it. I just didnāt know how.
Last weekend, I sat down with the PowerShell prompt and Google, and I set to work on writing a script. When I tackle problems, I like to break it down into smaller problems. It makes it all a bit more manageable. Also, thereās a feeling of accomplishment as I solve the smaller problemsāinstead of being frustrated that I havenāt solve ātheā problem, I can be excited that I solved several problems, and still have another to solve.
Problem 1: How can I use PowerShell to generate some pure CPU workload?
When I have to do this in SQL Server, I can just have SQL Server do math, or parse strings (SQL Server sucks at parsing strings). PowerShell is pretty good at parsing strings, but maybe I can do the math thing? Some quick Google-fu, and the internet was in agreement. A common suggestion was this chunk of code:
$result = 1; foreach ($number in 1..2147483647) {$result = $result * $number};
So, whatās that doing? Well, itās basically doing multiplication for 1 times 2 times 3 times 4 times 5 times 6 timesā¦ all the way up to 2,147,483,647. Or more simply written: 2147483647! Calculating the factorial of such a large number is definitely going to require some computational power. Running the above line of code on my laptop definitely caused CPU to jump. Task Manager showed PowerShell using about 15% of my CPU. Cool! Problem 1 solved!
Problem 2: How can I run a bunch of these in parallel?
I actually knew the answer to this problem, Iād just never done it myself. PowerShell has a concept of jobs, which let you send a process to the background. This means I can start multiple jobs, all performing parallel worker threads computing (2147483647!). Beautiful. The PowerShell syntax for Start-Job
was straightforward, and the examples from the PowerShell docs were great. Problem 2 solved!
Problem 3: How many threads do I need?
I spent some time thinking about this. If I were writing a user story for a developer, Iād want to be able to request a desired āburn rateā for the CPU: āSend CPU to x%!ā
If I assume that each thread will burn about 1 CPU core, then generating 100% CPU load on my 4-core laptop means running one thread for every CPU. Similarly, generating 50% load would mean running a thread for 50% of the cores. I know how many cores my laptop has, but it would be cool if I didnāt have to. The script should figure that out for me.
Getting the number of cores is pretty easy in PowerShell with WMI. If I grab the number of cores, then itās quick math to calculate x%! Problem 3 solved! (Get-WmiObject āclass Win32_processor).NumberOfLogicalProcessors
Problem 4: How do I stop this thing?
Once I start burning CPUs on my machine, it might make my machine unstable. How do I stop it once I set this in motion?
I did some quick searching on what happens with PowerShell jobs, how to kill them, etc. One possibility is to just close the host window, and the child processes will die with it. But that seems like a crummy answer. And itās always a bad user experience to tell someone āyea, youāre just going to have to force quit the app to make it stop.ā The more elegant answer is to use the Stop-Job
cmdlet. Heck, I didnāt even need to look that up. PowerShell verbs are predictable, so the opposite of Start-Job
had to be Stop-Job
.
Problem 5: How do I stitch this together?
Iāll let you look at the script to see all the details: Itās called Submit-CpuWorkload, and is available on GitHub.
I want to pause and give a big thank-you to Drew Furgiuele (blog|twitter). I asked Drew to take a look at my primitive code, and he came back with a handful of suggestions to make it 1000 times better. I learned a bunch of new PowerShell things from Drew during that code review. If you have the chance to learn PowerShell from Drew, do it.
At itās simplest form, you just call it and pass in a percentage. For example : Submit-CpuWorkload -UtilizeCorePercent 75
will generate one worker thread for 75% of the number of logical cores. I built in an āAre you sure?ā confirmation so that I donāt accidentally hurt myself or my computer, so before the CPU Workload starts, Iāll have to hit Y.
But, what if I want to script this? Interactive āAre you sure?ā prompts donāt work for scripting. Since Iām using PowerShellās built-in āConfirmImpactā stuff (thanks, Drew!), I can just tack on -Confirm:$false
as a parameter (as in, ādonāt confirmā) and it will skip the interactive prompt.
After running Stop-Job a bunch of times manually, I wanted a way to kill & clean up those CPU-burning jobs. I added this to the script under a -Cleanup parameter. Behind the scenes, this does Stop-Job
then Receive-Job -AutoRemoveJob
to clean things up. Supplying the -Cleanup parameter will only kill jobs, it wonāt start any: Submit-CpuWorkload -Cleanup $true
Lastly, I took Drewās suggestion and made the script return an object that contains the list of jobs that currently exist (or that just got cleaned up). This is handy output to see as a user, but Drew also explained how it makes it trivial to now use this as a building block in a larger process. That does sound handy, and was easy enough to doāeasier to do now while the script is fresh than trying to do it next year when I need it.
Thatās it!
This exercise turned out to be a bit easier than I expected. I have a fun little script that I was able to share. It responds to that original request perfectly: generate CPU load on a server, outside of SQL Server. And the best part, I learned a lot in the process!
Got a better way? Let me know in the comments.