Scope:
face-engine-staging.aws.facetrustai.com, the real AWS Fargate deployment (2 vCPU / 4 GB, face_engine_desired_count = 2, no autoscaling), tested with k6 sending real JPEG uploads to POST /v1/embed — not a synthetic or local benchmark. See FaceTrust AI API’s load-testing page for how this fits alongside the other two services’ results.Methodology
Staged ramp: 2 → 5 → 10 → 20 → 30 VUs, 30-45s per stage. Each iteration randomly picked betweenGET /v1/health (33%, cheap) and POST /v1/embed with a real 230KB test JPEG (67%, the actual CPU-bound detection+embedding work).
Results
The surprising part: CPU never saturated
CloudWatch CPUUtilization for thefacetrust-staging-face-engine ECS service during the test window (2 tasks, 2 vCPU each):
CPU peaked at under 30%, on a service whose entire job is CPU-bound face detection + embedding. If ONNXRuntime were using the full 2 vCPU allocated per task, sustained concurrent inference should drive CPU well past 30% — this strongly suggests the runtime is not configured to use multiple threads (ONNXRuntime’s
intra_op_num_threads/inter_op_num_threads default to conservative values, sometimes effectively 1, unless explicitly set). Memory stayed low too (peak 10.3%), ruling that out as well.
Practical implication: the 22-second p95 latency under load is not a “need bigger instances” problem — it’s very likely a single-threaded inference bottleneck that more vCPU wouldn’t fix without also tuning ONNXRuntime’s thread-pool configuration in InferenceEngine.cpp/main.cpp. Horizontally scaling face_engine_desired_count beyond 2 would help in the meantime (more independent single-threaded workers), but the underlying inefficiency — allocating 2 vCPU per task and using a fraction of one — is worth fixing before spending more on compute.
What this doesn’t cover
- Only
/v1/embedwas load tested, not/v1/compareor/v1/liveness— those exercise different code paths (/compareruns detection+embedding twice,/livenessruns the heuristic PAD checks). - Single image reused for every request — no test of decode-time variance across real-world image sizes/formats (the endpoint accepts arbitrary JPEG/PNG).
- No sustained soak test, only a single ~4-minute ramp up and down.