Performance
Purpose
Use this page to choose the fastest Clawperator calling pattern for repeated Android observations and actions.
Current guidance:
- use an emulator when the target app supports it
- use the Serve API for repeated caller traffic
- avoid tight loops that start a fresh
clawperatorprocess each time - treat physical USB results as device-and-transport-specific
Maintainers who need to reproduce the measurements should use the repo-local
test-io-speeds skill.
The skill owns release APK setup, device selection, timing logs, output
locations, and raw artifact handling.
Current Timing Snapshot
These values come from a test-io-speeds validation run after the 0.9.5
release, using release package com.clawperator.operator, 10 measured calls per
app, and these apps: Android Settings, Google Play Store, YouTube, and Google
Home.
| Path | Mean snapshot latency | Cost versus fastest | What it means |
|---|---|---|---|
| Emulator + direct daemon | about 78 ms | baseline | Fastest measured diagnostic path |
| Emulator + fresh CLI through daemon | about 220 ms | about +142 ms | Cost of starting/parsing/formatting a fresh clawperator process instead of reusing a persistent caller |
| Physical USB + direct daemon | about 197 ms | about +119 ms | Physical device and USB transport penalty versus emulator on the direct-daemon path |
| Physical USB + fresh CLI through daemon | about 326 ms | about +248 ms | Physical USB plus fresh CLI overhead together |
Average deltas from the same run:
| Comparison | Observed delta |
|---|---|
| Fresh CLI overhead on emulator | about +142 ms |
| Fresh CLI overhead on physical USB | about +129 ms |
| Physical USB penalty versus emulator, direct daemon path | about +119 ms |
| Physical USB penalty versus emulator, fresh CLI path | about +106 ms |
Detailed per-app means from the same run:
| Device path | Settings | Play Store | YouTube | Google Home |
|---|---|---|---|---|
| Emulator fresh CLI | 199.7 ms | 198.8 ms | 283.9 ms | 196.1 ms |
| Emulator direct daemon | 78.4 ms | 76.3 ms | 77.9 ms | 78.7 ms |
| Physical USB fresh CLI | 293.1 ms | 298.0 ms | 371.0 ms | 340.1 ms |
| Physical USB direct daemon | 187.1 ms | 214.3 ms | 188.1 ms | 197.9 ms |
What Direct Daemon Means
Direct daemon is a diagnostic measurement path. It means the timing harness
posts the execution payload directly to the already-running Clawperator daemon
over the daemon's internal Unix socket, using that internal server's /execute
route.
Direct daemon is not the public Serve API POST /execute,
and it is not the same as running clawperator execute from a fresh shell. It
bypasses fresh CLI process startup, CLI argument parsing, stdout formatting, and
other per-command CLI wrapper work.
Do not build user-facing skills or integrations against the daemon's internal Unix socket. Use the Serve API for persistent caller traffic.
Calling Patterns
| Path | Command surface | Persistence | Use for |
|---|---|---|---|
| Fresh CLI through daemon | clawperator snapshot |
Starts a new process for each call, then proxies through the daemon when available | Human terminal use and one-off checks |
| Serve API | clawperator serve, then HTTP requests such as POST /snapshot |
One long-running process | Repeated agent loops and throughput-sensitive callers |
| Direct daemon socket | Internal daemon Unix socket under ~/.clawperator/daemon/ |
One daemon process | Diagnostics only |
| Direct execution | --no-daemon or CLAWPERATOR_NO_DAEMON=1 |
No daemon proxy | Debugging daemon behavior |
Fresh CLI means each call starts a new clawperator process, parses CLI
arguments, resolves configuration, checks or starts the daemon, proxies the
request, parses the result, formats stdout, and exits. The daemon still helps,
but the caller pays per-process overhead.
Serve means the caller starts one local HTTP server and sends repeated requests to that same process. This avoids fresh process startup and CLI formatting on every call.
Replace Fresh CLI Observe-Act-Observe
A common slow skill pattern is observe, act, then observe again by shelling out for each step:
before="$(clawperator snapshot)"
if printf '%s' "$before" | grep -q 'Activate'; then
clawperator click --text "Activate"
fi
after="$(clawperator snapshot)"
printf '%s' "$after" | grep -q 'Activated'
That starts a fresh process for each observation and action. In retries or bounded polling, the cost repeats.
Instead, start one Serve API process:
clawperator serve
Then keep the caller process alive. Use
POST /snapshot for observation:
before="$(curl -s http://127.0.0.1:3000/snapshot \
-H 'Content-Type: application/json' \
-d '{}')"
When ready to act and verify, send the action and verification as one
POST /execute payload:
curl -s http://127.0.0.1:3000/execute \
-H 'Content-Type: application/json' \
-d @- <<'JSON'
{
"execution": {
"commandId": "activate-flow",
"taskId": "activate-flow",
"source": "performance-doc",
"expectedFormat": "android-ui-automator",
"timeoutMs": 30000,
"actions": [
{
"id": "tap-activate",
"type": "click",
"params": { "matcher": { "textEquals": "Activate" } }
},
{
"id": "wait-activated",
"type": "wait_for_node",
"params": {
"matcher": { "textContains": "Activated" },
"timeoutMs": 15000
}
},
{ "id": "verify", "type": "snapshot" }
]
}
}
JSON
This mirrors real skill structure: observe, click by selector, wait for the target UI state, and capture the final snapshot, while keeping process startup out of the repeated path.
Practical Guidance
- Use an emulator baseline when the target app supports emulator use.
- Use
clawperator servefor repeated caller API traffic. - Use fresh CLI timing only when measuring terminal command experience.
- Keep physical-device measurements as compatibility and transport data.
- Re-measure with the
test-io-speedsskill after changes to snapshot, transport, daemon proxying, output formatting, or Android operator timing.