mix dala.battery_bench_android (dala_dev v0.0.3)

Copy Markdown View Source

Builds a benchmark APK, deploys it, and measures battery drain over time.

Run this from your Dala app project directory (the one containing android/, 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"

Reports mAh every 10 seconds and prints a summary at the end. WiFi ADB is required for accurate measurements (USB cable charges the battery).

Setup (one-time, while plugged in)

adb -s SERIAL tcpip 5555
adb connect PHONE_IP:5555
# then unplug and pass PHONE_IP:5555 as --device

Same two-step pattern as iOS — push BEAM flags via mix dala.deploy, then bench with --no-build. Lets you change tuning without a Gradle rebuild.

# 1. Push BEAM flags via dala.deploy (no APK rebuild — ~10 sec).
mix dala.deploy --beam-flags "" --android              # tuned (Nerves)
mix dala.deploy --beam-flags "-S 4:4 -A 8" --android   # untuned variant

# 2. Run the bench with --no-build.
mix dala.battery_bench_android --no-build --device 192.168.1.42:5555

See README.md for the full rationale and recovery procedure.

Usage (with built-in Gradle build path)

mix dala.battery_bench_android
mix dala.battery_bench_android --no-beam
mix dala.battery_bench_android --preset nerves
mix dala.battery_bench_android --flags "-sbwt none -S 1:1"
mix dala.battery_bench_android --duration 3600 --device 192.168.1.42:5555
mix dala.battery_bench_android --no-build   # re-run without rebuilding

Options

  • --duration N — benchmark duration in seconds (default: 1800 = 30 min)
  • --device SERIAL — adb device serial or IP:port (auto-detected if omitted)
  • --no-beam — baseline: build without starting the BEAM at all
  • --no-keep-alive — skip the foreground-service background keep-alive call
  • --preset NAME — named BEAM flag preset (Gradle-build path only)
  • --flags "..." — arbitrary BEAM VM flags (Gradle-build path only)
  • --no-build — skip APK build and install; run benchmark on current install
  • --log-path PATH — override CSV log location (default: _build/bench/run_android_<ts>.csv)
  • --no-csv — skip CSV logging
  • --skip-preflight — bypass the preflight checks (adb/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

The BEAM with Nerves-style tuning flags uses roughly the same power as an app with no BEAM at all (~200 mAh/hr on a Moto G, 30-min run). The untuned BEAM uses ~25% more power due to scheduler busy-waiting. For most apps the overhead is in the noise; tune if you have stricter power budgets.

Under the hood

mix dala.battery_bench_android orchestrates the following adb and Gradle commands:

# Build and install
./gradlew assembleDebug [-PextraCppFlags="-DNO_BEAM|..."]
adb install -r app/build/outputs/apk/debug/app-debug.apk

# Push BEAMs
adb push _build/dev/lib/*/ebin/*.beam /data/data/<pkg>/files/otp/<app>/

# Reset battery stats and launch
adb shell dumpsys batterystats --reset
adb shell am start -n <pkg>/.MainActivity

# Turn screen off
adb shell input keyevent 26          # KEYCODE_POWER

# Poll battery every 10s
adb shell dumpsys battery            # reads "Charge counter: <µAh>"

# Stop app and collect final reading
adb shell am force-stop <pkg>
adb shell dumpsys battery

BEAM tuning flags are injected as C preprocessor defines (-DBEAM_UNTUNED, -DBEAM_FULL_NERVES, etc.) or via a generated dala_beam_flags.h header, so each preset compiles a different variant of the BEAM startup C code.