12. Power Optimization
Chapter 12 of 18 · 20 min
Power consumption determines battery life for mobile and IoT deployments. Neural network inference power draw varies dramatically based on hardware utilization, memory bandwidth, and compute intensity. Understanding power profiles enables informed tradeoffs between performance and battery life.
Measuring power consumption on Raspberry Pi:
# Using vcgencmd (Broadcom GPU tools)
import subprocess
def read_power_draw():
"""Read instantaneous power draw"""
try:
result = subprocess.run(
['vcgencmd', 'power_status'],
capture_output=True, text=True
)
return result.stdout.strip()
except FileNotFoundError:
return None
# Continuous power monitoring
def monitor_power(duration_sec=60, interval_ms=100):
import time
measurements = []
start = time.time()
while time.time() - start < duration_sec:
measurements.append(read_power_draw())
time.sleep(interval_ms / 1000)
return measurements
# Parse power measurements
# Expected output: "power_on=1 is_rail_enabled=1 rail=AXP173-lifeext"
Android power profiling uses Battery Historian:
# Enable detailed battery debugging
adb shell dumpsys batterystart
adb shell dumpsys batterystats --enable full-wake-tracker
# Run inference workload
adb shell dumpsys batterystats > battery_stats.txt
adb pull /data/batterystats/battery-history.txt
# Process with battery historian docker image
docker run --rm -v $(pwd):/battery-history:ro \
gcr.io/android-battery-historian/stable --parse=battery-history.txt
CPU frequency scaling impacts power linearly:
# Check available governors
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# Output: conservative ondemand userspace powersave performance schedutil
# Set to performance for maximum throughput
sudo bash -c 'for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo performance > $f
done'
# Set to powersave for minimum consumption
sudo bash -c 'for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo powersave > $f
done'
Power optimization through batching reduces per-sample energy:
def measure_energy_inference(interpreter, input_data, batch_size):
"""Power model: E = P_active * t_processing + P_idle * t_idle"""
from time import perf_counter
# Energy during computation
t_start = perf_counter()
interpreter.invoke()
t_compute = perf_counter() - t_start
# Estimate energy assuming 5W active, 0.5W idle
energy_active = 5.0 * t_compute # Joules
energy_idle = 0.5 * 0.5 # 500mW idle 500ms
energy_per_sample = energy_active / batch_size
return energy_per_sample, energy_active + energy_idle
Dark wake prevention for mobile:
// Use WorkManager for inference to avoid app suspension
val constraints = Constraints.Builder()
.setRequiresBatteryNotLow(true)
.build()
val inferenceRequest = OneTimeWorkRequestBuilder<InferenceWorker>()
.setConstraints(constraints)
.build()
WorkManager.getInstance(context)
.enqueue(inferenceRequest)
// Worker implementation to avoid wake locks
class InferenceWorker: CoroutineWorker() {
override suspend fun doWork(): Result {
return try {
runInference()
Result.success()
} catch (e: Exception) {
Result.retry()
}
}
}
EXERCISE
Profile power consumptionIdle versus inference-active states on an edge device, calculate energy per inference sample, and determine optimal batch size for power efficiency.