Deep Dive: Power Attribution Algorithms on Bare Metal
This issue analyzes the algorithms used by Kepler, Scaphandre, and vrapl for attributing power consumption to VMs/containers when running on bare metal with RAPL access.
1. Scaphandre Algorithm
Core Formula
Process_Power = (Process_CPU_Time / Total_CPU_Time) × Total_Power_from_RAPL
Detailed Algorithm
Step 1: Data Collection
- Read RAPL energy counters from /sys/class/powercap/intel-rapl
- Read global CPU time from /proc/stat (in jiffies)
- Read per-process CPU time from /proc/<PID>/stat (in jiffies)
Step 2: Calculate Ratios
CPU_Ratio = Process_Jiffies / Total_Active_Jiffies
Step 3: Attribution
Process_Energy = Total_Energy_Delta × CPU_Ratio
Process_Power = Process_Energy / Time_Interval
Implementation Details
Jiffy-based Timing:
- Uses Linux kernel time units (jiffies)
- Typical jiffy = 10ms (100 Hz) or 4ms (250 Hz)
- Reads from
/proc/stat for system-wide CPU time
- Reads from
/proc/<PID>/stat for process-specific time
Energy Sources:
- Primary: RAPL via powercap subsystem
- Supports: CPU package, DRAM, uncore domains
- Sampling interval: User-configurable (default ~5 seconds)
Why This Approach?
Design Rationale:
- Low Overhead:
/proc reads are cheap, no kernel modifications
- Simplicity: Linear proportional model, easy to understand
- Platform Independence: Works on any Linux system with
/proc
- No Special Hardware: Only needs RAPL (standard on modern x86)
Developer Quote:
"This core mechanism combines time (jiffies) and energy (joules) to estimate process-level power consumption with minimal overhead."
Assumptions
- Linear Power-Time Relationship: Assumes power consumption scales linearly with CPU time
- Uniform Instruction Power: All CPU instructions consume roughly the same power
- CPU Time = Energy: Ignores instruction-level power differences
- Idle Power: Currently doesn't explicitly handle idle power attribution
- Single Allocation Key: Uses only CPU time (doesn't account for memory, I/O)
Constraints
Technical:
- Requires read access to
/sys/class/powercap (root or proper permissions)
- RAPL must be available (Intel Sandy Bridge+, AMD Zen+)
- Linux kernel 5.4.0-53.59+: powercap only accessible by root
- Must load
intel_rapl or intel_rapl_common kernel module
Accuracy:
- Jiffy granularity limits precision
/proc/stat updates at HZ rate (100-1000 Hz)
- RAPL updates ~1000 Hz but with smoothing
- Proportional model breaks down when CPU time != energy
Known Limitations
Issue #20: CPU usage sum can exceed 100% × CPU count, leading to power sum > RAPL total
Issue #38: "The main allocation key is CPU time, and as host level power metrics include more components on the machine, this allocation key becomes more inaccurate."
From CNCF Blog (2023):
"Future versions should include keys regarding the activity of other components than CPU."
2. Kepler Algorithm
Core Formula (Ratio Power Model)
Container_Power = Idle_Power_Share + Dynamic_Power_Share
Where:
Dynamic_Power_Share = (Container_Resource_Utilization / Total_Resource_Utilization) × Dynamic_Power
Idle_Power_Share = (Container_Size / Total_Container_Size) × Idle_Power
Detailed Algorithm
Step 1: Collect Hardware Metrics via eBPF
Per-container metrics:
- CPU instructions (performance counter)
- CPU cycles (performance counter)
- Cache misses (last-level cache)
- CPU time (scheduler stats)
- Memory access patterns
Step 2: Calculate Dynamic Power
Total_Power = RAPL_Energy_Delta / Time_Interval
Node_CPU_Usage = Active_CPU_Time / Total_CPU_Time
Dynamic_Power = Total_Power × Node_CPU_Usage
Step 3: Calculate Idle Power
Idle_Power = Total_Power × (1 - Node_CPU_Usage)
Step 4: Attribute Dynamic Power
Container_Dynamic = Dynamic_Power × (Container_Instructions / Total_Instructions)
Note: Can use cycles, cache misses, or CPU time - configurable
Step 5: Attribute Idle Power (GHG Protocol)
Container_Idle = Idle_Power × (Container_Memory_Size / Total_Memory_Allocated)
Step 6: Total Attribution
Container_Total_Power = Container_Dynamic + Container_Idle
Implementation Details
eBPF Collection:
// Pseudo-code for eBPF program
struct process_metrics {
u64 cpu_cycles;
u64 cpu_instructions;
u64 cache_references;
u64 cache_misses;
u64 cpu_time_us;
};
// Hook into scheduler events
int on_sched_switch(struct task_struct *prev, struct task_struct *next) {
// Read hardware performance counters
metrics.cpu_cycles = read_perf_counter(PERF_COUNT_HW_CPU_CYCLES);
metrics.cpu_instructions = read_perf_counter(PERF_COUNT_HW_INSTRUCTIONS);
metrics.cache_misses = read_perf_counter(PERF_COUNT_HW_CACHE_MISSES);
// Store per-container
update_container_metrics(next->cgroup, &metrics);
}
Hardware Performance Counters:
PERF_COUNT_HW_CPU_CYCLES: Total CPU cycles
PERF_COUNT_HW_INSTRUCTIONS: Retired instructions
PERF_COUNT_HW_CACHE_REFERENCES: Cache accesses
PERF_COUNT_HW_CACHE_MISSES: Last-level cache misses
PERF_COUNT_HW_BRANCH_MISSES: Branch prediction failures
Why This Approach?
Design Rationale:
- Instruction-Level Accuracy: Different instructions consume different power
- Memory Awareness: Cache misses indicate memory-bound workloads
- Idle Power Handling: Explicit idle power attribution
- Kubernetes Native: Built for container orchestration
- Cloud-Friendly: Can fall back to ML models
Developer Discussion (#548):
"CPU time only explains part of the energy usage... the nature of the CPU instructions accounts for the delta between different types of workloads when their CPU time are the same."
Research Finding:
"There is evidence that CPU instructions can estimate power well"
Assumptions
- Instruction-Power Correlation: CPU instructions better correlate with power than time
- Linear Dynamic Power: Dynamic power scales with resource utilization
- Idle Power by Size: Idle power should be split by container memory size (GHG Protocol)
- Hardware Counters Available: Assumes access to performance counters
- Stable Workloads: Models trained on specific workload patterns
Constraints
Technical:
- Requires eBPF support (Linux kernel 4.14+)
- Needs access to perf events subsystem
- Performance counters must be exposed (may not be in VMs)
- Kernel with BPF LSM or appropriate security settings
- cgroup v2 for container tracking
Accuracy:
- ML models require training data
- Estimator accuracy varies by workload type
- Idle power attribution is controversial
- Hardware counter overhead (though minimal with eBPF)
Known Limitations
From 2024 Research Paper:
"Kepler's metrics are not accurate where total cluster power is concerned... the estimates exhibit consistency for showing variations between runs of the same workload, but they are approximations of actual power consumption."
Idle Power Challenge (CNCF Blog 2024):
"Idle power attribution is challenging in public cloud environment where computation is shared among multiple tenants, and its contribution to total power for a node is significant and can range from 20% to 60% of the power at maximum utilization."
Discussion #1284:
"Dynamic power consumption is directly related to resource utilization, but determining CPU utilization can vary based on the type of instructions and cache operations, with CPU cycles typically exhibiting better correlation."
3. vrapl Algorithm
Core Formula
VM_Power = (VM_CPU_Time / Total_CPU_Time) × Host_RAPL_Power
Detailed Algorithm
Step 1: Discover VM Process
// Use libvirt to get VM UUID
uuid := libvirt.GetVMUUID(vmName)
// Find QEMU process with that UUID
for process in allProcesses {
if process.cmdline.contains(uuid) {
vmPid = process.pid
}
}
Step 2: Collect Metrics
// Host CPU time (all cores)
hostCPU = readCPUTimes() // user + system time
hostTotal = hostCPU.user + hostCPU.system
// VM process CPU time
vmCPU = readProcessTimes(vmPid) // via gopsutil
vmTotal = vmCPU.user + vmCPU.system
// Host RAPL energy
raplEnergy = readFile("/sys/class/powercap/intel-rapl:0/energy_uj")
Step 3: Calculate Deltas
deltaHostCPU = currentHostTotal - lastHostTotal
deltaVMCPU = currentVMTotal - lastVMTotal
deltaEnergy = currentRaplEnergy - lastRaplEnergy
Step 4: Attribution
if deltaHostCPU > 0 {
vmCPUFraction = deltaVMCPU / deltaHostCPU
vmEnergy = deltaEnergy * vmCPUFraction
vmPower = vmEnergy / timeInterval / 1_000_000 // Convert μJ to W
}
Step 5: Communication
// Stream to VM via serial port
serialPort.WriteString(fmt.Sprintf("vRAPL_Watts: %.2f\n", vmPower))
Why This Approach?
Design Rationale:
- Simplicity: Easiest possible implementation
- Direct Communication: Serial port avoids filesystem complexity
- Real-time: Continuous streaming, no polling
- Single Purpose: Focused on VM attribution only
- Educational: Clear code for learning
Assumptions
- CPU Time Proportionality: Same as Scaphandre
- Single VM Focus: Designed for monitoring one VM
- Serial Port Available: VM has virtio-serial configured
- QEMU/KVM: Uses libvirt, assumes QEMU hypervisor
- Package-Level RAPL: Uses first RAPL package found
Constraints
Technical:
- Requires host access (cannot run inside VM)
- Needs libvirt connection
- Serial port must be configured in VM
- RAPL access (same as Scaphandre)
- Linux host only
Accuracy:
- Same limitations as CPU-time-based approaches
- No idle power handling
- No memory/IO attribution
- Single RAPL zone (doesn't handle multi-socket)
Algorithm Comparison Matrix
| Feature |
vrapl |
Scaphandre |
Kepler |
| Allocation Metric |
CPU time |
CPU time (jiffies) |
CPU instructions* |
| Idle Power |
❌ No |
❌ No (planned) |
✅ Yes (by size) |
| Dynamic Power |
✅ Proportional |
✅ Proportional |
✅ Ratio model |
| Memory Attribution |
❌ No |
❌ No |
⚠️ Indirect (idle) |
| I/O Attribution |
❌ No |
❌ No |
❌ No |
| Hardware Counters |
❌ No |
❌ No |
✅ Yes (eBPF) |
| Instruction Awareness |
❌ No |
❌ No |
✅ Yes |
| Cache Miss Tracking |
❌ No |
❌ No |
✅ Yes |
| Multi-Socket |
⚠️ Single zone |
✅ Yes |
✅ Yes |
| Overhead |
🟢 Minimal |
🟢 Low |
🟡 Medium (eBPF) |
| Complexity |
🟢 ~200 LOC |
🟡 ~10K LOC |
🔴 ~50K LOC |
| Data Source |
/proc, gopsutil |
/proc/stat |
eBPF + perf |
| Sampling Rate |
2s (configurable) |
~5s (configurable) |
~3s (configurable) |
* Kepler is configurable - can use cycles, instructions, or CPU time
Accuracy Analysis
CPU Time vs CPU Instructions
The Debate (from Kepler Discussion #548):
Pro CPU Instructions (Kepler):
"CPU time only explains part of the energy usage... the nature of the CPU instructions accounts for the delta between different types of workloads when their CPU time are the same."
- Different instructions have different power draw
- Integer ops < Floating point < SIMD/AVX
- Cache misses cause memory accesses (higher power)
- Branch mispredictions waste energy
Pro CPU Time (Scaphandre/vrapl):
"The approach by Scaphandre using elapsed jiffies is at least off in absolute value from harder metrics like Instructions."
- Simpler to implement and understand
- Lower overhead (no performance counters)
- Works reliably across all systems
- Good enough for comparative analysis
Empirical Evidence
From 2024 Research:
- Kepler: "Estimates exhibit consistency for showing variations... but they are approximations"
- Scaphandre: "Generally accurate enough for comparative analysis"
Both approaches are approximations. Neither is perfect.
When Each Works Best
CPU Time Better For:
- Uniform workloads (web servers, databases)
- Comparative analysis (before/after)
- Relative measurements (container A vs B)
- Systems without hardware counters
CPU Instructions Better For:
- Heterogeneous workloads (mixed int/float)
- Absolute power values
- Memory-bound vs CPU-bound distinction
- Fine-grained attribution
Pros and Cons
vrapl
Pros:
- ✅ Simplest implementation possible
- ✅ Easy to understand and modify
- ✅ Minimal dependencies
- ✅ Real-time serial streaming
- ✅ No filesystem complexity
- ✅ Perfect for learning/teaching
Cons:
- ❌ Single VM only
- ❌ No idle power handling
- ❌ No memory/IO attribution
- ❌ CPU time limitations (same as Scaphandre)
- ❌ No Prometheus export
- ❌ No multi-socket support
Best For:
- Development/testing environments
- Single VM monitoring
- Learning VM power attribution
- Simple deployments
Scaphandre
Pros:
- ✅ Production-ready and mature
- ✅ Multiple exporters (Prometheus, JSON, Riemann)
- ✅ Multi-process/VM support
- ✅ Low overhead
- ✅ Active community
- ✅ Comprehensive documentation
- ✅ Multi-socket support
Cons:
- ❌ CPU time limitations (see Issue #20)
- ❌ No idle power handling yet
- ❌ No instruction-level awareness
- ❌ Filesystem sharing for VMs (more complex than serial)
- ❌ Sum of process power can exceed RAPL total
- ❌ Single allocation key (CPU time)
Best For:
- Production monitoring
- Multi-process environments
- Integration with monitoring stacks
- When simplicity matters more than instruction-level accuracy
Kepler
Pros:
- ✅ Most sophisticated approach
- ✅ Instruction-level power awareness
- ✅ Idle power attribution (GHG Protocol)
- ✅ Hardware performance counters
- ✅ Cache miss tracking
- ✅ Kubernetes-native
- ✅ ML fallback for cloud
- ✅ Active CNCF project
Cons:
- ❌ Most complex implementation
- ❌ Requires eBPF (kernel 4.14+)
- ❌ Requires performance counters
- ❌ ML models need training
- ❌ Higher overhead than alternatives
- ❌ Idle power attribution controversial
- ❌ Accuracy issues documented (2024 research)
- ❌ Container-focused, not VM-optimized
Best For:
- Kubernetes environments
- Heterogeneous workloads
- When instruction-level accuracy needed
- Cloud environments (with ML fallback)
- Organizations with ML/data science teams
Recommendations Based on Use Case
Choose vrapl when:
- 🎯 Monitoring a single VM
- 🎯 Learning VM power attribution
- 🎯 Development/testing environment
- 🎯 Need simplest possible solution
- 🎯 Want to customize the code easily
Choose Scaphandre when:
- 🎯 Production bare-metal deployment
- 🎯 Multiple VMs/processes to monitor
- 🎯 Need Prometheus integration
- 🎯 Simplicity and reliability are priorities
- 🎯 CPU time attribution is "good enough"
Choose Kepler when:
- 🎯 Kubernetes cluster
- 🎯 Container workloads
- 🎯 Need instruction-level accuracy
- 🎯 Heterogeneous workloads (ML, HPC)
- 🎯 Cloud deployment (can use ML estimator)
- 🎯 Have resources for eBPF/ML complexity
Improving vrapl Based on This Analysis
Quick Wins (Keep Simplicity):
- Add multi-VM support (monitor N VMs simultaneously)
- Add DRAM RAPL domain (memory attribution)
- Add Prometheus exporter (compatibility)
- Handle RAPL counter overflow
Medium Term (Add Sophistication):
- Implement idle power attribution (Kepler approach)
- Track VM memory size for better idle split
- Add multi-socket RAPL support
- Benchmark vs Scaphandre accuracy
Advanced (Optional):
- Add eBPF for instruction counting (Kepler approach)
- Hybrid model: CPU time + instructions
- ML estimator for cloud deployment
- Per-vCPU attribution
References
Scaphandre
Kepler
General
Deep Dive: Power Attribution Algorithms on Bare Metal
This issue analyzes the algorithms used by Kepler, Scaphandre, and vrapl for attributing power consumption to VMs/containers when running on bare metal with RAPL access.
1. Scaphandre Algorithm
Core Formula
Detailed Algorithm
Step 1: Data Collection
Step 2: Calculate Ratios
Step 3: Attribution
Implementation Details
Jiffy-based Timing:
/proc/statfor system-wide CPU time/proc/<PID>/statfor process-specific timeEnergy Sources:
Why This Approach?
Design Rationale:
/procreads are cheap, no kernel modifications/procDeveloper Quote:
Assumptions
Constraints
Technical:
/sys/class/powercap(root or proper permissions)intel_raplorintel_rapl_commonkernel moduleAccuracy:
/proc/statupdates at HZ rate (100-1000 Hz)Known Limitations
Issue #20: CPU usage sum can exceed 100% × CPU count, leading to power sum > RAPL total
Issue #38: "The main allocation key is CPU time, and as host level power metrics include more components on the machine, this allocation key becomes more inaccurate."
From CNCF Blog (2023):
2. Kepler Algorithm
Core Formula (Ratio Power Model)
Detailed Algorithm
Step 1: Collect Hardware Metrics via eBPF
Step 2: Calculate Dynamic Power
Step 3: Calculate Idle Power
Step 4: Attribute Dynamic Power
Note: Can use cycles, cache misses, or CPU time - configurable
Step 5: Attribute Idle Power (GHG Protocol)
Step 6: Total Attribution
Implementation Details
eBPF Collection:
Hardware Performance Counters:
PERF_COUNT_HW_CPU_CYCLES: Total CPU cyclesPERF_COUNT_HW_INSTRUCTIONS: Retired instructionsPERF_COUNT_HW_CACHE_REFERENCES: Cache accessesPERF_COUNT_HW_CACHE_MISSES: Last-level cache missesPERF_COUNT_HW_BRANCH_MISSES: Branch prediction failuresWhy This Approach?
Design Rationale:
Developer Discussion (#548):
Research Finding:
Assumptions
Constraints
Technical:
Accuracy:
Known Limitations
From 2024 Research Paper:
Idle Power Challenge (CNCF Blog 2024):
Discussion #1284:
3. vrapl Algorithm
Core Formula
Detailed Algorithm
Step 1: Discover VM Process
Step 2: Collect Metrics
Step 3: Calculate Deltas
Step 4: Attribution
Step 5: Communication
Why This Approach?
Design Rationale:
Assumptions
Constraints
Technical:
Accuracy:
Algorithm Comparison Matrix
* Kepler is configurable - can use cycles, instructions, or CPU time
Accuracy Analysis
CPU Time vs CPU Instructions
The Debate (from Kepler Discussion #548):
Pro CPU Instructions (Kepler):
Pro CPU Time (Scaphandre/vrapl):
Empirical Evidence
From 2024 Research:
Both approaches are approximations. Neither is perfect.
When Each Works Best
CPU Time Better For:
CPU Instructions Better For:
Pros and Cons
vrapl
Pros:
Cons:
Best For:
Scaphandre
Pros:
Cons:
Best For:
Kepler
Pros:
Cons:
Best For:
Recommendations Based on Use Case
Choose vrapl when:
Choose Scaphandre when:
Choose Kepler when:
Improving vrapl Based on This Analysis
Quick Wins (Keep Simplicity):
Medium Term (Add Sophistication):
Advanced (Optional):
References
Scaphandre
Kepler
General