Site icon Andy M Mallon ā€“ AMĀ²

Generating artificial CPU load šŸ”„

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?


I quickly replied:

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.

Exit mobile version