10

An application I'm contributing to fires up a component written in C.

The C process does some pretty heavy crunching and if your not careful can really hammer your CPU.

Is there a way to set a limit to external processes spawned by .NET?

I've seen this artivcle on setting a hard memory limit at the OS level, is there a similar thing for CPU?

Daniel Upton
  • 5,561
  • 8
  • 41
  • 64
  • 3
    You can change the process's priority (I think it may even be an option in `ProcessStartInfo`). It doesn't really limit how much processor time it'll get but it will tell the OS let other processes take priority. – M.Babcock Mar 20 '12 at 19:16

2 Answers2

12

I had the same problem. I solved it by using SetInformationJobObject Kernel32 Win Api and JOBOBJECT_CPU_RATE_CONTROL_INFORMATION struct.

My bigger problem was to represent this structure in c# (uses "union"). Hopefully, I found a "mono" description of this structure.

[StructLayout(LayoutKind.Explicit)]
//[CLSCompliant(false)]
struct JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
{
    [FieldOffset(0)]
    public UInt32 ControlFlags;
    [FieldOffset(4)]
    public UInt32 CpuRate;
    [FieldOffset(4)]
    public UInt32 Weight;
}

To activate the Cpu limitation :

ControlFlags = 0x00000001 | 0x00000004;
CpuRate = percent of max usage  * 100 (ex 50 * 100 for a 50% limit)

The sample below is fully functional.

I hope that's help.

Kind Regards

-Thierry-

[DllImport("kernel32.dll", EntryPoint = "CreateJobObjectW", CharSet = CharSet.Unicode)]
public static extern IntPtr CreateJobObject(SecurityAttributes JobAttributes, string lpName);

[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AssignProcessToJobObject(IntPtr hJob, IntPtr hProcess);

[DllImport("kernel32.dll")]
static extern bool SetInformationJobObject(IntPtr hJob, JOBOBJECTINFOCLASS JobObjectInfoClass, IntPtr lpJobObjectInfo, uint cbJobObjectInfoLength);

public class SecurityAttributes
{

    public int nLength; 
    public IntPtr pSecurityDescriptor; 
    public bool bInheritHandle;

    public SecurityAttributes()
    {
        this.bInheritHandle = true;
        this.nLength = 0;
        this.pSecurityDescriptor = IntPtr.Zero;
    }
}

public enum JOBOBJECTINFOCLASS
{
    JobObjectAssociateCompletionPortInformation = 7,
    JobObjectBasicLimitInformation = 2,
    JobObjectBasicUIRestrictions = 4,
    JobObjectEndOfJobTimeInformation = 6,
    JobObjectExtendedLimitInformation = 9,
    JobObjectSecurityLimitInformation = 5,
    JobObjectCpuRateControlInformation = 15
}

[StructLayout(LayoutKind.Explicit)]
//[CLSCompliant(false)]
struct JOBOBJECT_CPU_RATE_CONTROL_INFORMATION
{
    [FieldOffset(0)]
    public UInt32 ControlFlags;
    [FieldOffset(4)]
    public UInt32 CpuRate;
    [FieldOffset(4)]
    public UInt32 Weight;
}

public enum CpuFlags
{
    JOB_OBJECT_CPU_RATE_CONTROL_ENABLE = 0x00000001,
    JOB_OBJECT_CPU_RATE_CONTROL_WEIGHT_BASED = 0x00000002,
    JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP = 0x00000004
}

/// <summary>
/// Launch the legacy application with some options set.
/// </summary>
static void DoExecuteProgramm()
{
    // prepare the process to execute
    var startInfo = new ProcessStartInfo();
    . . . . . 
    // Start the process
    var process = Process.Start(startInfo);

    //Limit the CPU usage to 45%
    var jobHandle = CreateJobObject(null, null);
    AssignProcessToJobObject(jobHandle, process.Handle);
    var cpuLimits = new JOBOBJECT_CPU_RATE_CONTROL_INFORMATION();
    cpuLimits.ControlFlags = (UInt32)(CpuFlags.JOB_OBJECT_CPU_RATE_CONTROL_ENABLE | CpuFlags.JOB_OBJECT_CPU_RATE_CONTROL_HARD_CAP);
    cpuLimits.CpuRate = 45 * 100; // Limit CPu usage to 45%
    var pointerToJobCpuLimits = Marshal.AllocHGlobal(Marshal.SizeOf(cpuLimits));
    Marshal.StructureToPtr(cpuLimits, pointerToJobCpuLimits, false);
    if (!SetInformationJobObject(jobHandle, JOBOBJECTINFOCLASS.JobObjectCpuRateControlInformation, pointerToJobCpuLimits, (uint)Marshal.SizeOf(cpuLimits)))
    {
        Console.WriteLine("Error !");
    }
}
Thierry LG.
  • 121
  • 1
  • 4
  • Fascinating, I always forget about Windows Job objects, could be quite useful. Unfortunately the `JobObjectCpuRateControlInformation` flag is only supported in Win8 and later. – Chris O Jul 11 '15 at 21:05
  • Unfortunately, yes.... I used it for processes management in a Windows 2012 server (security requirement). – Thierry LG. Jul 11 '15 at 21:16
  • Thank you for your code . This code works for me but I just wonder I set Limit CPu usage to 45% but it uses 54-50 % ... do you know why ?! – Mohammad Sina Karvandi Mar 12 '16 at 18:58
  • @MohammadSinaKarvandi I'm coming late, but that's likely because the rate limitation doesn't apply while the process is in kernel mode. – NovHak May 26 '22 at 03:51
9

Not in Windows. You can lower the process priority though, which will reduce the likelihood that the problematic process will be scheduled on the CPU and interfere with other (presumably higher priority) applications. For instance, from http://dotnet-concepts-queries-interviews.blogspot.com/2007/05/how-to-set-process-priority-in-net.html:

Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.BelowNormal;

Keep in mind, if you don't have anything else running on the box, you probably want this process to consume all of the available CPU.

You can also set the CPU affinity if it is on a multi-processor box, limiting the processing to certain cores and leaving others free for other applications. Generally the OS does a good job of scheduling application threads though, so setting the process priority is likely to have a better overall result. See How Can I Set Processor Affinity in .NET?

Community
  • 1
  • 1
Chris Shain
  • 50,833
  • 6
  • 93
  • 125
  • 4
    It's worth noting **this answer is incorrect** given current versions of Windows; this can be done via `Job Object` APIs in Win2012/Win8, at the time this question was answered you could use `NtSetInformationProcess` 'QUOTA_LIMITS_EX::CPU_RATE_LIMIT' in Vista/Win7/Win2008. Please review other answers and online material before giving up. Further, setting the priority class also affects IO rates, not just CPU rates, which may have unintended effects if your goal was NOT to degrade IO throughput. – Shaun Wilson Aug 31 '15 at 22:21
  • 1
    @ShaunWilson Do you have a working code sample showing this? I haven't yet managed to get this working. – i3arnon Dec 29 '15 at 16:08