This week brought quite a few challenges. One of them was a question asked by a friend:
How do I search contents of all the files for given string, without killing the performance of the computer?
This seemed like a simple question to answer: Just lower the priority of the PowerShell process to Idle.
The only problem is, that it does not work.
That piece of code actually lowers the priority of the process. But this priority only applies to compute-bound tasks. In other words, this option is great if we don’t want the CPU to run on 100 %, but it won’t help us much with disk operations.
To confirm that you can run the following code on Idle priority and view the PowerShell.exe in Task Manager.
On my system I can see
PowerShell.exe exhausting 90-100 % of disk I/O resources.
At this point I was not sure if I am even setting that priority correctly so I used ProcessExplorer to check the priority and spotted this:
Now I wonder, how do I lower the priority for I/O bound operations? My first was checking the enum of values you can set to the PriorityClass:
As you can see, nothing specific to IO. No luck there. Let’s Google, because I remember reading about this in Windows via C++.
The solution to this problem is setting the priority of the process to
PROCESS_MODE_BACKGROUND_BEGIN, but unfortunately .NET does not offer this functionality, so we’ll need to P/Invoke. Pretty easy to do in PowerShell. Just create a piece of C# code that’s compiled on runtime using the Add-Type cmdlet and you should be good to go.
In the previous code I am creating a static class (to make it easy to call), and importing single method from kernel32.dll. That method is called SetPriorityClass and is well documented on MSDN.aspx). I also define an enum of priorities, which defines the
PROCESS_MODE_BACKGROUND_BEGIN, but I could also use the value directly and explain it in a comment.
Other than that I define a public method SetBackgroundIoPriority which does all the work and sets the current process IO priority to Very Low. In the last three lines of PowerShell code I am just calling that public method and throwing an exception that is not very helpful :D
Running the same code as above now exhausts 1-3 % of my disk at best.
(It goes without saying that the script runs a lot longer now, but that’s a tradeoff for letting the foreground work to be done first.)