---
title: Benchmarks
description: A BenchmarkJob runs genai-bench against an InferenceService with the traffic scenarios you choose, and stores the results.
---
A BenchmarkJob measures how an [InferenceService](inference-services.md) performs under load. OME runs [genai-bench](https://github.com/sgl-project/genai-bench) against it with the traffic shapes and concurrency levels you choose, and genai-bench saves its throughput and latency results to the storage you name. Use one to see how much load a replica takes before latency climbs, or to compare runtimes and GPU types. [Run benchmarks](../../guides/deploy-models/run-benchmarks.md) walks through a run from start to results.
The examples use the `llama-demo` namespace and the `llama-3-2-1b-instruct` InferenceService from [A minimal InferenceService](inference-services.md#a-minimal-inferenceservice).
## The BenchmarkJob resource
A benchmark runs in iterations. Each iteration sends one traffic scenario at one concurrency level, and stops after `maxTimePerIteration` minutes or `maxRequestsPerIteration` requests, whichever comes first.
This file creates a PersistentVolumeClaim for the results, and a BenchmarkJob that runs two scenarios at three concurrency levels: six iterations of up to 10 minutes.
```yaml title="benchmark.yaml"
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: benchmark-results
namespace: llama-demo
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
---
apiVersion: ome.io/v1beta1
kind: BenchmarkJob
metadata:
name: llama-3-2-1b-instruct-bench
namespace: llama-demo
spec:
endpoint:
inferenceService:
name: llama-3-2-1b-instruct
namespace: llama-demo
task: text-to-text
trafficScenarios:
- "D(100,100)"
- "N(480,240)/(300,150)"
numConcurrency:
- 1
- 4
- 16
maxTimePerIteration: 10
maxRequestsPerIteration: 200
outputLocation:
storageUri: pvc://benchmark-results/llama-3-2-1b-instruct
podOverride:
image: ghcr.io/sgl-project/genai-bench:0.1.132
```
OME releases don't publish the genai-bench image that the `ome-resources` chart sets by default, so the example uses the image that the genai-bench project publishes. To make it the default, set the chart values `ome.benchmarkJob.image` to `ghcr.io/sgl-project/genai-bench` and `ome.benchmarkJob.tag` to `0.1.132`. The claim uses your cluster's default StorageClass. Apply the file:
```bash
kubectl apply -f benchmark.yaml
```
```output
persistentvolumeclaim/benchmark-results created
benchmarkjob.ome.io/llama-3-2-1b-instruct-bench created
```
The spec's fields:
| Field | What it sets |
| --- | --- |
| `endpoint` | Required. The InferenceService or URL to benchmark. See [Endpoints](#endpoints). |
| `task` | Required. The kind of requests: `text-to-text`, `image-to-text`, `text-to-embeddings` or `image-to-embeddings`. |
| `maxTimePerIteration` | Required. How many minutes an iteration runs at most. |
| `maxRequestsPerIteration` | Required. How many requests an iteration sends at most. |
| `outputLocation` | Required. Where the results go. See [Where results go](#where-results-go). |
| `trafficScenarios` | The sizes of the requests. See [Traffic scenarios](#traffic-scenarios). Without it, genai-bench uses its defaults. |
| `numConcurrency` | How many requests genai-bench keeps in flight, one level per iteration. Without it, genai-bench uses its defaults. |
| `resultFolderName` | The folder that holds the results. See [Where results go](#where-results-go). |
| `serviceMetadata` | Labels for the results: `engine` (`vLLM`, `SGLang` or `TGI`), `version`, `gpuType` (`H100`, `A100`, `MI300` or `A10`) and `gpuCount`. Set all four. |
| `huggingFaceSecretReference` | A Secret in the BenchmarkJob's namespace. Its `HUGGINGFACE_API_KEY` key sets the pod's `HUGGINGFACE_API_KEY` variable. |
| `podOverride` | Changes to the benchmark pod. See [How a benchmark runs](#how-a-benchmark-runs). |
`additionalRequestParams` and `dataset` have no effect: OME doesn't pass them to genai-bench. The [API reference](../../reference/api/ome.v1beta1.md#ome-io-v1beta1-BenchmarkJobSpec) describes every field.
## Endpoints
`spec.endpoint` names one target: an `inferenceService` or an `endpoint` URL. Admission rejects a BenchmarkJob that sets neither or both. You can benchmark an InferenceService whose model is stored on the nodes.
### An InferenceService
`name` and `namespace` are required, and the InferenceService can be in another namespace. OME fills in the rest from it:
| genai-bench gets | From the InferenceService |
| --- | --- |
| Base URL | The scheme and host of `status.url`, such as `http://llama-3-2-1b-instruct.llama-demo.svc.cluster.local:8080` |
| API format | `openai`, or the value of `PROTOCOL_VERSION` in `spec.engine.runner.env` |
| Model name | Since v1.3, the engine's `--served-model-name`, or else its `--model` or `--model-path`: `vllm-model` in the example. On v1.2.2, always `vllm-model`, so the runtime must serve the model under that name. |
| Tokenizer | The model's `storage.path`, mounted read-only from the node |
| API key | The placeholder `sample-key` |
The pod runs only on nodes where the model is `Ready`, as [Node labels and status](../models/base-models.md#node-labels-and-status) describes. OME can't benchmark a model with a `pvc://` storage URI, because no node holds a copy of it. Since v1.3, it also can't benchmark an InferenceService that names only a runtime.
### An external endpoint
`endpoint.endpoint` takes a base `url`, an `apiFormat` (`openai`, `oci-cohere` or `cohere`) and the `modelName` the endpoint serves. A BenchmarkJob that targets a URL becomes `Failed`: genai-bench needs a tokenizer, and an API key for `openai`, and a BenchmarkJob has no field for either. This is a known bug.
## Traffic scenarios
A traffic scenario sets the size of each request, in tokens or images. The forms for each task:
| Scenario | Tasks | Requests |
| --- | --- | --- |
| `D(,