Performance Baselines

Overview

This document defines performance baselines for StellaOps scanner operations. All metrics are measured against reference images and workloads to ensure consistent, reproducible benchmarks.

Last Updated: 2025-12-14 Next Review: 2026-03-14


Reference Images

Standard images used for performance benchmarking:

ImageSizeComponentsExpected VulnsCategory
alpine:3.197MB~15~5Minimal
debian:12-slim75MB~90~40Minimal
ubuntu:22.0477MB~100~50Standard
node:20-alpine180MB~200~100Application
python:3.121GB~300~150Application
mcr.microsoft.com/dotnet/aspnet:8.0220MB~150~75Application
nginx:1.25190MB~120~60Application
postgres:16-alpine240MB~140~70Database

Scan Performance Targets

Container Image Scanning

Image CategoryP50 TimeP95 TimeMax MemoryCPU Cores
Minimal (<100MB)< 5s< 10s< 256MB1
Standard (100-500MB)< 15s< 30s< 512MB2
Large (500MB-2GB)< 45s< 90s< 1.5GB2
Very Large (>2GB)< 120s< 240s< 2GB4

Per-Image Targets

ImageP50 TimeP95 TimeMax Memory
alpine:3.19< 3s< 8s< 200MB
debian:12-slim< 8s< 15s< 300MB
ubuntu:22.04< 10s< 20s< 400MB
node:20-alpine< 20s< 40s< 600MB
python:3.12< 35s< 70s< 1.2GB
dotnet/aspnet:8.0< 25s< 50s< 800MB
nginx:1.25< 18s< 35s< 500MB
postgres:16-alpine< 22s< 45s< 600MB

Reachability Analysis Targets

By Codebase Size

Codebase SizeP50 TimeP95 TimeMemoryNotes
Tiny (<5k LOC)< 10s< 20s< 256MBSingle service
Small (5-20k LOC)< 30s< 60s< 512MBSmall service
Medium (20-50k LOC)< 2min< 4min< 1GBTypical microservice
Large (50-100k LOC)< 5min< 10min< 2GBLarge service
Very Large (100-500k LOC)< 15min< 30min< 4GBMonolith
Monorepo (>500k LOC)< 45min< 90min< 8GBEnterprise monorepo

By Language

LanguageRelative SpeedNotes
Go1.0x (baseline)Fast due to simple module system
Java1.2xMaven/Gradle resolution adds overhead
C#1.3xMSBuild/NuGet resolution
TypeScript1.5xnpm/yarn resolution, complex imports
Python1.8xVirtual env resolution, dynamic imports
JavaScript2.0xComplex bundler configurations

SBOM Generation Targets

FormatP50 TimeP95 TimeOutput SizeNotes
CycloneDX 1.7 (JSON)< 1s< 3s~50KB/100 componentsStandard (Baseline with 1.6; 1.7 performance expected similar - re-benchmark pending)
CycloneDX 1.7 (XML)< 1.5s< 4s~80KB/100 componentsVerbose (Baseline with 1.6; 1.7 performance expected similar - re-benchmark pending)
SPDX 3.0.1 (JSON)< 1s< 3s~60KB/100 componentsStandard
SPDX 3.0.1 (Tag-Value)< 1.2s< 3.5s~70KB/100 componentsLegacy format

Combined Operations

OperationP50 TimeP95 Time
Scan + SBOMscan_time + 1sscan_time + 3s
Scan + SBOM + Reachabilityscan_time + reach_time + 2sscan_time + reach_time + 5s
Full attestation pipelinetotal_time + 2stotal_time + 5s

VEX Processing Targets

OperationP50 TimeP95 TimeNotes
VEX document parsing< 50ms< 150msPer document
Lattice state computation< 100ms< 300msPer 100 vulnerabilities
VEX consensus merge< 200ms< 500ms3-5 sources
State transition< 10ms< 30msSingle transition

CVSS Scoring Targets

OperationP50 TimeP95 TimeNotes
MacroVector lookup< 1μs< 5μsDictionary lookup
CVSS v4.0 base score< 10μs< 50μsFull computation
CVSS v4.0 full score< 20μs< 100μsBase + threat + env
Vector parsing< 5μs< 20μsString parsing
Receipt generation< 100μs< 500μsIncludes hashing
Batch scoring (100 vulns)< 5ms< 15msParallel processing

Attestation Targets

OperationP50 TimeP95 TimeNotes
DSSE envelope creation< 50ms< 150msIncludes signing
DSSE verification< 30ms< 100msSignature check
Rekor submission< 500ms< 2sNetwork dependent
Rekor verification< 300ms< 1sNetwork dependent
in-toto predicate< 20ms< 80msJSON serialization

Database Operation Targets

OperationP50 TimeP95 TimeNotes
Receipt insert< 5ms< 20msSingle record
Receipt query (by ID)< 2ms< 10msIndexed lookup
Receipt query (by tenant)< 10ms< 50msIndex scan
EPSS lookup (single)< 1ms< 5msIndexed
EPSS lookup (batch 100)< 10ms< 50msBatch query
Risk score insert< 5ms< 20msSingle record
Risk score update< 3ms< 15msSingle record

Regression Thresholds

Performance regression is detected when metrics exceed these thresholds compared to baseline:

MetricWarning ThresholdBlocking ThresholdAction
P50 Time> 15% increase> 25% increaseBlock release
P95 Time> 20% increase> 35% increaseBlock release
Memory Usage> 20% increase> 30% increaseBlock release
CPU Time> 15% increase> 25% increaseInvestigate
Throughput> 10% decrease> 20% decreaseBlock release

Regression Detection Rules

  1. Warning: Alert engineering team, add to release notes
  2. Blocking: Cannot merge/release until resolved or waived
  3. Waiver: Requires documented justification and SME approval

Measurement Methodology

Environment Setup

# Standard test environment
# - CPU: 8 cores (x86_64)
# - Memory: 16GB RAM
# - Storage: NVMe SSD
# - OS: Ubuntu 22.04 LTS
# - Docker: 24.x

# Clear caches before cold start tests
docker system prune -af
sync && echo 3 > /proc/sys/vm/drop_caches

CLI surface. The binary is stella, not stellaops. scan and sbom are command groups, not leaf commands — stella scan --image … exits 2 with Unrecognized command or argument '--image'. Verify any invocation below against stella <verb> --help before quoting a number produced by it.

Scan Performance

Image scanning is two steps: generate a deterministic SBOM, then match it against the compact vuln DB. Time them separately — they have different cost profiles.

# Cold start measurement (SBOM generation)
time stella sbom generate --image alpine:3.19 --format cyclonedx --output sbom.json --force

# Warm cache measurement (run 3x, take average)
for i in {1..3}; do
  time stella sbom generate --image alpine:3.19 --format cyclonedx --output sbom.json --force
done

# Offline match against the compact vuln DB (no backend required)
time stella sbom check --sbom sbom.json --format json > /dev/null

# Memory profiling
/usr/bin/time -v stella sbom generate --image alpine:3.19 --format cyclonedx \
  --output sbom.json --force 2>&1 | grep "Maximum resident set size"

# CPU profiling
perf stat stella sbom check --sbom sbom.json --format json > /dev/null

--source-date pins the SBOM timestamp, which is what makes repeat runs byte-identical; leave it at the default only when you are not comparing digests across runs.

Reachability Analysis

Reachability is measured over a call graph extracted from source. stella scan graph does the extraction; stella reachability operates on the result.

# Time measurement — call-graph extraction
time stella scan graph --lang dotnet --target ./src --output callgraph.json

# Memory profiling
/usr/bin/time -v stella scan graph --lang dotnet --target ./src --output callgraph.json 2>&1

# Reachability over an existing SBOM, in one pass (offline engine)
time stella sbom check --sbom sbom.json --reachability --call-graph callgraph.json --format json > /dev/null

--lang accepts dotnet, java, node, python, go, rust, ruby, php. There is no csharp value — .NET projects use dotnet. stella sbom check --reachability additionally accepts --source + --reach-lang (go|rust only) to extract a graph in-process instead of taking a pre-extracted one.

SBOM Generation

# Time measurement
time stella sbom generate --image node:20-alpine --format cyclonedx --output sbom.json --force

# Output size
stella sbom generate --image node:20-alpine --format cyclonedx --output sbom.json --force && \
  ls -lh sbom.json

Batch Operations

There is no CLI flag for parallel multi-image scanning. stella sbom generate takes a single --image or --directory; drive concurrency from the shell.

# Process multiple images in parallel (4 at a time)
time xargs -P 4 -I{} sh -c \
  'stella sbom generate --image "$1" --format cyclonedx --output "./results/$(echo "$1" | tr "/:" "__").json" --force' _ {} \
  < images.txt

# Throughput test (images per minute)
START=$(date +%s)
for i in {1..10}; do
  stella sbom generate --image alpine:3.19 --format cyclonedx --output /tmp/sbom.json --force > /dev/null
done
END=$(date +%s)
echo "Throughput: $(( 10 * 60 / (END - START) )) images/minute"

stella scan submit-list --file <ids.txt> --max-concurrent 10 does exist and is throttled, but it takes already-registered SBOM source ids (GUIDs), not image references, and it requires a running backend — it is not a substitute for the local loop above.


CI Integration

Benchmark Workflow

# .gitea/workflows/performance-benchmark.yml
name: Performance Benchmark

on:
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * 1'  # Weekly Monday 2am

jobs:
  benchmark:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Run benchmarks
        run: python src/Tools/StellaOps.Bench/Determinism/run_bench.py --output results/

      - name: Aggregate results
        run: |
          dotnet run --project src/Tools/Bench/StellaOps.Bench.Compute.Cli -- \
            report results/ --output results/report.json

      - name: Upload results
        uses: actions/upload-artifact@v4
        with:
          name: benchmark-results
          path: results/

The regression gate is not shipped. Earlier revisions of this page ran stellaops benchmark compare --baseline … --fail-on-regression and make benchmark-performance. Neither exists: stella benchmark exits 2 (Unrecognized command or argument 'benchmark') and the repository has no root Makefile. The thresholds in the tables above are targets a reviewer applies by reading the report — nothing enforces them automatically today.

Real harnesses, for anyone wiring this up properly:

  • src/Tools/StellaOps.Bench/Determinism/run_bench.py and run_reachability.py — Python, offline-friendly (offline_run.sh).
  • src/Tools/StellaOps.Bench/{Graph,ImpactIndex,PolicyCache,PolicyDelta}/ — per-subsystem microbenchmarks.
  • src/Tools/Bench/StellaOps.Bench.Compute.Cli — binary name stella-bench-compute, verbs discovery-truth, evidence-score, compare-metrics, report. It is a separate tool from stella; the comparison it does is scanner-vs-scanner discovery coverage, not run-over-run performance regression.

Verify the exact flags with --help before pasting any of this into CI.

Local Testing

# Run full benchmark suite
make benchmark-performance

# Run specific image benchmark
make benchmark-image IMAGE=alpine:3.19

# Generate baseline
make benchmark-baseline

# Compare against baseline
make benchmark-compare

Optimization Guidelines

For Scan Performance

  1. Pre-pull images for consistent timing
  2. Use layered caching for repeat scans
  3. Enable parallel analysis for multi-ecosystem images
  4. Consider selective scanning for known-safe layers

For Reachability

  1. Incremental analysis for unchanged files
  2. Cache resolved dependencies
  3. Use language-specific optimizations (e.g., Roslyn for C#)
  4. Limit call graph depth for very large codebases

For Memory

  1. Stream large SBOMs instead of loading fully
  2. Use batched database operations
  3. Release intermediate data structures early
  4. Configure GC appropriately for workload

Historical Baselines

Version History

VersionDateP50 Scan (alpine)P50 Reach (50k LOC)Notes
1.3.02025-12-14TBDTBDCurrent
1.2.02025-09-01TBDTBDPrevious
1.1.02025-06-01TBDTBDBaseline

Improvement Targets

QuarterFocus AreaTargetStatus
Q1 2026Scan cold start-20%Planned
Q1 2026Reachability memory-15%Planned
Q2 2026SBOM generation-10%Planned

References