Repository: ollama/ollama
Pull request: runner: Set windows above normal priority for consistent CPU inference performance

When running the subprocess as a background service windows may throttle, which can lead to thrashing and very poor token rate.

Fixes #3511 

I've now reproduced the performance problem and understand the nature of what's going on leading to poor CPU inference performance for some users on Windows.

Windows treats GUI apps and background services differently.  By default, priority is given to GUI apps ("Programs")

![image](https://github.com/user-attachments/assets/33f6d5f4-74d5-4d35-9e33-6c3d45865687)

While you can change this, this wouldn't typically be recommended.  The result is when the tray app is automatically started when user logs in, or by clicking "Ollama" from the start menu, it runs as a "background service" which the subprocess inherits.  In some situations, this can lead to the subprocess runner being throttled, and since we try to create as many threads as there are cores, this results in thrashing behavior, and very poor token rates.

![image](https://github.com/user-attachments/assets/55207d05-4db3-4180-8d8d-9775bb531049)

By setting the scheduler priority class to "above normal", this allows more CPU usage, and higher CPU load, leading to much better token rates.

![image](https://github.com/user-attachments/assets/70d958ab-3492-4419-9130-5895bbcd1294)
![image](https://github.com/user-attachments/assets/2f2c0ff8-69ad-4062-bdc1-aad28cd0235f)


I also tried setting the scheduler priority to "high" (not recommended in the API docs) and that did lead to the UI freezing, and after ~1 minute, a BSOD and reboot due to a watchdog detecting the system becoming unresponsive.  The setting of "Above Normal" does not appear to have any noticeable impact on UI performance, and token rates match when the ollama server is running from a terminal (and inherits the "Program" priority). I do see the CPU utilization drop down slightly over time, so it seems Windows does penalize CPU saturating background processes so for very large context size requests there may still be room to improve performance further.


Reference:
- https://learn.microsoft.com/en-us/windows/win32/procthread/scheduling-priorities#priority-class

Changed files:
filename: llm/llm_windows.go
patch: @@ -4,13 +4,19 @@ import (
 	"syscall"
 )
 
-const CREATE_DEFAULT_ERROR_MODE = 0x04000000
+const (
+	CREATE_DEFAULT_ERROR_MODE   = 0x04000000
+	ABOVE_NORMAL_PRIORITY_CLASS = 0x00008000
+)
 
 var LlamaServerSysProcAttr = &syscall.SysProcAttr{
 	// Wire up the default error handling logic If for some reason a DLL is
 	// missing in the path this will pop up a GUI Dialog explaining the fault so
 	// the user can either fix their PATH, or report a bug. Without this
 	// setting, the process exits immediately with a generic exit status but no
 	// way to (easily) figure out what the actual missing DLL was.
-	CreationFlags: CREATE_DEFAULT_ERROR_MODE,
+	//
+	// Setting Above Normal priority class ensures when running as a "background service"
+	// with "programs" given best priority, we aren't starved of cpu cycles
+	CreationFlags: CREATE_DEFAULT_ERROR_MODE | ABOVE_NORMAL_PRIORITY_CLASS,
 }