⚡ Performance Optimization Guide
Table of Contents
- Identifying Performance Issues
- Understanding Prompt Rendering
- Segment Caching Strategies
- Disabling Expensive Segments
- Optimization Techniques
- Benchmarking
- Pre-Built Optimized Configurations
- Troubleshooting Slow Prompts
Identifying Performance Issues
Symptoms of Performance Problems
- Delayed prompt rendering (> 300ms before first character appears)
- CPU spikes when entering new directory
- Laggy typing (terminal unresponsive while typing)
- Battery drain on laptops
- Network delays over SSH
Quick Performance Check
# Measure prompt generation time
$sw = [System.Diagnostics.Stopwatch]::StartNew()
oh-my-posh print primary
$sw.Elapsed.TotalMilliseconds
# Goal: < 200ms
# Acceptable: < 300ms
# Problem: > 500ms
Enable Debug Timing
# Enable Oh My Posh debug output
$env:OHMYPOSH_DEBUG = "true"
# Reload profile
& $profile
# Check which segments are slow
# Output will show timing for each segment
Understanding Prompt Rendering
Rendering Pipeline
┌─────────────────────────────────────────────┐
│ User presses Enter │
└────────────┬────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ Oh My Posh initializes │ ~10ms
└────────────┬────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ For each block: │
│ For each segment: │
│ ├─ Check cache │ ~1-5ms
│ ├─ Execute segment (if not cached) │ ~5-100ms
│ └─ Format output │ ~1-2ms
└────────────┬────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ Render prompt string │ ~5-10ms
└────────────┬────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ Total time: 30-300+ ms │
└─────────────────────────────────────────────┘
Segment Execution Cost
| Segment Type | Typical Cost | Cacheable |
|---|---|---|
| shell | 1-2ms | ✅ Yes |
| path | 5-10ms | ⚠️ Sometimes |
| git | 50-200ms | ✅ Yes (by folder) |
| status | 1-2ms | ✅ Yes |
| command | 10-100ms+ | ✅ Yes |
| node | 30-80ms | ✅ Yes |
| python | 30-80ms | ✅ Yes |
| owm (weather) | 500ms+ | ⚠️ Network |
Segment Caching Strategies
Cache Strategy Types
{
"cache": {
"strategy": "session|folder|windows",
"duration": "5m",
"skip_cache": false
}
}
Strategy: Session
Cache for entire shell session (from startup to close).
Best for: Runtime versions, environment variables, fixed state
Example:
{
"cache": {
"strategy": "session",
"duration": "1h" // Effectively entire session
},
"type": "node"
}
Pros: ✅ Fastest, ✅ Consistent Cons: ❌ Won’t detect version changes mid-session
Strategy: Folder
Cache per directory (resets when changing directories).
Best for: Git status, file-dependent info
Example:
{
"cache": {
"strategy": "folder",
"duration": "5m"
},
"type": "git"
}
Pros: ✅ Updates on directory change, ✅ Good balance Cons: ⚠️ May still be slow in large repos
Strategy: Windows
Cache per PowerShell window session.
Best for: OS-specific info
Example:
{
"cache": {
"strategy": "windows"
},
"type": "env"
}
Duration Formatting
| Format | Meaning | Use Case |
|---|---|---|
"500ms" |
500 milliseconds | Very frequent updates |
"2s" |
2 seconds | Time display |
"30s" |
30 seconds | Active development |
"5m" |
5 minutes | Git status in slow repos |
"1h" |
1 hour | Runtime versions |
Recommended Caching Configuration
{
"blocks": [
{
"segments": [
{
"type": "shell",
"cache": {
"strategy": "session",
"duration": "1h"
}
},
{
"type": "path",
"cache": {
"strategy": "session",
"duration": "1h"
}
},
{
"type": "git",
"cache": {
"strategy": "folder",
"duration": "5m"
},
"properties": {
"fetch_status": true,
"fetch_upstream_icon": false // Expensive
}
},
{
"type": "node",
"cache": {
"strategy": "folder",
"duration": "30m"
}
},
{
"type": "command",
"cache": {
"strategy": "session",
"duration": "5m"
}
}
]
}
]
}
Disabling Expensive Segments
Method 1: Empty Template
Hide a segment without removing it:
{
"properties": {
"api_key": "..." // Won't execute
},
"template": "", // Empty = hidden
"type": "owm"
}
Method 2: Conditional Rendering
Only show in certain directories:
{
"template": "{{ if or .Error .Detached }}{{ .Branch }}{{ end }}",
"type": "git"
}
Method 3: Remove Expensive Properties
{
"properties": {
"fetch_status": false, // Expensive network call
"fetch_upstream_icon": false, // Upstream tracking
"fetch_worktree_count": false // Worktree checking
},
"type": "git"
}
High-Cost Segments to Consider Disabling
owm(Weather) - Network-dependent- 500ms+ per call
- Solution: Disable or cache 30m+
gitwith fetch_status - Large repo scanning- 100-500ms in large repos
- Solution:
fetch_status: false
commandwith expensive scripts - Custom logic- 50-200ms depending on script
- Solution: Cache aggressively or replace
node,python,rubywithout caching - File system scans- 30-80ms per segment
- Solution: Add proper caching
Optimization Techniques
1. Parallel Segment Execution
Oh My Posh 7.9+ supports parallel segment execution:
{
"version": 3,
"parallel": true, // Enable parallel rendering
"blocks": [...]
}
Impact: 20-40% faster on average
2. Simplified Git Status
For large repositories:
{
"cache": {
"strategy": "folder",
"duration": "10m"
},
"properties": {
"fetch_status": false,
"fetch_upstream_icon": false,
"windows_registry": false
},
"type": "git"
}
3. Remove Unnecessary Segments
Analyze each segment:
- Do I need this information in my prompt?
- How often do I look at it?
- What’s the actual cost vs. benefit?
Example: Remove right-aligned segments if not needed
{
"blocks": [
{
"alignment": "left",
"segments": [...]
},
// {
// "alignment": "right",
// "segments": [...] // Disabled
// }
]
}
4. Use Transient Prompt
Show simplified prompt after command execution:
{
"transient_prompt": {
"background": "transparent",
"foreground": "p:accent_color",
"template": "❯ ",
"type": "prompt"
}
}
Effect: Only expensive prompt on fresh input
5. Optimize Custom Commands
{
"cache": {
"strategy": "folder",
"duration": "5m"
},
"properties": {
"shell": "powershell",
// ❌ SLOW: Searches entire filesystem
// "command": "Get-ChildItem -Recurse | Measure-Object"
// ✅ FAST: Only checks current directory
"command": "(Get-ChildItem | Measure-Object).Count"
},
"template": "{{ .Output }}",
"type": "command"
}
6. Reduce Path Depth
{
"properties": {
"max_depth": 2, // Show only last 2 directories
"folder_separator_icon": "/"
},
"type": "path"
}
Benchmarking
Measuring Individual Segment Performance
# Minimal test theme with single segment
$testTheme = @"
{
"version": 3,
"blocks": [{
"alignment": "left",
"segments": [{
"type": "git",
"background": "#000000",
"foreground": "#FFFFFF",
"template": "{{ .Branch }}",
"properties": {
"fetch_status": false,
"fetch_upstream_icon": false
}
}]
}]
}
"@
$testTheme | Out-File -FilePath "test.json"
# Benchmark
$times = @()
for ($i = 0; $i -lt 10; $i++) {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
oh-my-posh print primary --config test.json | Out-Null
$sw.Stop()
$times += $sw.Elapsed.TotalMilliseconds
}
$average = ($times | Measure-Object -Average).Average
$max = ($times | Measure-Object -Maximum).Maximum
$min = ($times | Measure-Object -Minimum).Minimum
Write-Host "Average: ${average}ms"
Write-Host "Min: ${min}ms"
Write-Host "Max: ${max}ms"
Full Theme Benchmark
# Compare full theme performance over time
$results = @()
for ($run = 0; $run -lt 5; $run++) {
$times = @()
for ($i = 0; $i -lt 10; $i++) {
$sw = [System.Diagnostics.Stopwatch]::StartNew()
oh-my-posh print primary --config "OhMyPosh-Atomic-Custom.json" | Out-Null
$sw.Stop()
$times += $sw.Elapsed.TotalMilliseconds
}
$avg = ($times | Measure-Object -Average).Average
$results += @{
Run = $run + 1
Average = [Math]::Round($avg, 2)
}
}
$results | Format-Table
Expected Performance
| Configuration | Time | Notes |
|---|---|---|
| Minimal (2-3 segments) | 30-50ms | ✅ Excellent |
| Standard (5-7 segments) | 50-150ms | ✅ Good |
| Full-featured | 150-300ms | ⚠️ Acceptable |
| Poorly optimized | 500ms+ | ❌ Needs work |
Pre-Built Optimized Configurations
Minimum Prompt (Fastest)
{
"blocks": [
{
"alignment": "left",
"segments": [
{
"type": "text",
"template": "❯ ",
"foreground": "p:accent"
}
]
}
],
"version": 3
}
Performance: ~5-10ms
Quick Prompt (Very Fast)
{
"blocks": [
{
"alignment": "left",
"segments": [
{
"type": "shell",
"cache": { "strategy": "session" }
},
{
"type": "path",
"properties": { "max_depth": 2 },
"cache": { "strategy": "session" }
},
{
"type": "git",
"properties": { "fetch_status": false },
"cache": { "strategy": "folder", "duration": "5m" }
}
]
}
],
"version": 3
}
Performance: ~30-50ms
Balanced Prompt (Good Performance)
{
"blocks": [
{
"alignment": "left",
"segments": [
{ "type": "shell", "cache": { "strategy": "session" } },
{ "type": "path", "cache": { "strategy": "session" } },
{
"type": "git",
"cache": { "strategy": "folder", "duration": "5m" },
"properties": {
"fetch_status": true,
"fetch_upstream_icon": false
}
},
{
"type": "node",
"cache": { "strategy": "folder", "duration": "30m" }
},
{ "type": "status", "cache": { "strategy": "session" } }
]
}
],
"version": 3
}
Performance: ~80-150ms
Troubleshooting Slow Prompts
Issue: Prompt Slow on First Directory Enter
Cause: First-time git status evaluation or caching not working
Solutions:
-
Increase git cache duration:
{ "cache": { "strategy": "folder", "duration": "10m" // Longer cache }, "type": "git" } -
Disable expensive git features:
{ "properties": { "fetch_status": false, "fetch_upstream_icon": false } }
Issue: Slow Over SSH/Network
Cause: Network latency amplifies segment delays
Solutions:
-
Aggressive caching for remote:
{ "cache": { "strategy": "folder", "duration": "30m" // Very long cache }, "type": "git" } -
Disable network-dependent segments:
{ "template": "", // Disable weather "type": "owm" } -
Simplify path display:
{ "properties": { "max_depth": 1 }, // Only current dir "type": "path" }
Issue: Slow in Large Git Repositories
Cause: Git status checking takes time in large repos
Solutions:
-
Disable status checking:
{ "properties": { "fetch_status": false }, "type": "git" } -
Use
.gitignoreto exclude untracked files:# .gitignore in repo root # This speeds up git status node_modules/ dist/ build/ -
Increase git cache significantly:
{ "cache": { "duration": "30m" // Cache for 30 minutes } }
Issue: High CPU Even with Caching
Cause: Caching not working or disabled
Solutions:
-
Verify cache strategy exists:
{ "cache": { "strategy": "folder", "duration": "5m", "skip_cache": false // Make sure not skipped } } - Check for multiple expensive segments
- Profile with debug:
$env:OHMYPOSH_DEBUG = "true"
Performance Tuning Checklist
- Measured baseline prompt time (< 300ms?)
- Enabled parallel execution (
"parallel": true) - Configured cache strategies for all segments
- Disabled
fetch_statusin git if repo is large - Removed unnecessary segments
- Set appropriate cache durations
- Tested performance over SSH
- Profiled with debug enabled
- Verified caching working (ls -la
.cache)
Summary
Performance Optimization Priority:
- Enable caching - ~50% improvement
- Disable expensive features - ~30% improvement
- Remove unnecessary segments - ~20% improvement
- Optimize custom commands - ~10-20% improvement
- Enable parallel execution - ~20-40% improvement
Target: < 200ms for typical usage, < 300ms maximum
For more details on specific segment configuration, see ADVANCED-CUSTOMIZATION-GUIDE.md.