Builds a benchmark app, deploys it to a physical iPhone/iPad, and measures battery drain over time.
Run this from your Dala app project directory (the one containing ios/,
dala.exs, and your Elixir source). It requires bundle_id to be set in
dala.exs:
config :dala_dev,
dala_dir: "/path/to/dala",
bundle_id: "com.example.myapp"Prerequisites
libimobiledevice is required for battery readings:
brew install libimobiledeviceThe device must be trusted on this Mac (accept "Trust This Computer" when
connecting via USB). Xcode 15 or later is required for xcrun devicectl.
Battery measurement
iOS exposes battery capacity via libimobiledevice's ideviceinfo tool.
If the device reports BatteryMaxCapacity (mAh), drain is shown in mAh like
the Android benchmark. Otherwise, it falls back to percentage points
(1% ≈ 40–60 mAh on most iPhones).
WiFi-only measurements are not possible on iOS — USB-connected readings are skewed because the cable can trickle-charge. To minimise this, use a USB-only data cable (no charging), or note the baseline with and without cable.
Recommended workflow (Dala projects)
Dala projects use ios/build_device.sh rather than a full Xcode project,
which means the bench task's xcodebuild path doesn't apply. Use this
two-step pattern instead:
# 1. Push BEAM flags via dala.deploy (no native rebuild — ~5 sec).
mix dala.deploy --beam-flags "" --ios # tuned (Nerves)
mix dala.deploy --beam-flags "-S 6:6 -A 8" --ios # untuned variant
# 2. Run the bench with --no-build, specifying the phone's WiFi IP.
mix dala.battery_bench_ios --no-build --wifi-ip 10.0.0.120Find the phone's WiFi IP in Settings → Wi-Fi → (i) → IP Address.
See README.md for the full rationale and recovery procedure if a flag
combination crashes the BEAM (which can happen if you request more
threads than iOS allows per process).
Usage (with built-in Xcode build path)
mix dala.battery_bench_ios
mix dala.battery_bench_ios --no-beam
mix dala.battery_bench_ios --preset nerves
mix dala.battery_bench_ios --flags "-sbwt none -S 1:1"
mix dala.battery_bench_ios --duration 3600 --device UDID
mix dala.battery_bench_ios --no-build # re-run without rebuildingOptions
--duration N— benchmark duration in seconds (default: 1800 = 30 min)--device UDID— device UDID (auto-detected if one device connected)--wifi-ip IP— phone's WiFi IPv4 (recommended; bypasses auto-discovery)--no-beam— baseline: build without starting the BEAM at all--no-keep-alive— skip the silent-audio background keep-alive call--preset NAME— named BEAM flag preset (Xcode-build path only)--flags "..."— arbitrary BEAM VM flags (Xcode-build path only)--no-build— skip Xcode build and install; benchmark current install--scheme NAME— Xcode scheme name (default: camelized app name)--log-path PATH— override CSV log location (default:_build/bench/run_<ts>.csv)--no-csv— skip CSV logging--skip-preflight— bypass the preflight checks (USB/app/BEAM/RPC/NIF/keep-alive)
What the presets do
untuned— raw BEAM with no tuning flags (highest power use baseline)sbwt— only busy-wait disabled (-sbwt none)nerves— full Nerves set: single scheduler + busy-wait off + multi_time_warp- (default) — same as
nerves(production default)
Understanding the results
iOS battery percentage resolution is coarse. A 30-minute run should produce 2–5% drain, enough to see the difference between no-BEAM and tuned-BEAM. Run at a fixed screen brightness or with the screen locked for reproducible results across runs.
Under the hood
mix dala.battery_bench_ios orchestrates the following commands:
# Build
xcodebuild -workspace ios/*.xcworkspace -scheme SCHEME \
-configuration Debug -sdk iphoneos \
-derivedDataPath /tmp/dala_bench_ios_STAMP [OTHER_CFLAGS='$(inherited) -DFLAG']
# Install and launch
xcrun devicectl device install app --device UDID /path/to/App.app
xcrun devicectl device process launch --terminate-existing --device UDID \
com.example.myapp # → captures PID
# Lock screen
idevicediagnostics -u UDID sleep
# Poll battery every 10s
ideviceinfo -u UDID -q com.apple.mobile.battery -k BatteryCurrentCapacity
ideviceinfo -u UDID -q com.apple.mobile.battery -k BatteryMaxCapacity
# Stop app
xcrun devicectl device process terminate --device UDID --pid PIDBEAM tuning flags are injected as OTHER_CFLAGS build settings passed to
xcodebuild, matching the same C preprocessor defines used in the Android build.