Cost and Time Estimation¶
Before embedding a dataset, estimate how long it will take and what it will cost. The fastest path is nova throughput-predict; the back-of-envelope arithmetic below is useful when you want a rough sense without running the predictor.
nova throughput-predict¶
The predictor reads dataset, model, text rendering, and per-text cutoff straight from your embedder YAML. It tokenizes a sample of the source to build an empirical length distribution, simulates batched padding, and prints throughput + wall-clock + cost estimates.
# Single-cutoff estimate
nova throughput-predict configs/embedder/finewiki_en_all_mini.yaml --gpu a10g
# Sweep across cutoffs to find the padding sweet spot
nova throughput-predict configs/embedder/finewiki_en_all_mini.yaml \
--gpu a10g --cutoff 256 --cutoff 512 --cutoff 1024
# Distributed wall-clock estimate (10× A10G in parallel)
nova throughput-predict configs/embedder/finewiki_en_all_mini.yaml \
--gpu a10g --num-gpus 10
# Save JSON + plot
nova throughput-predict configs/embedder/finewiki_en_all_mini.yaml \
--gpu a10g --output predictions.json
nova throughput-predict --help lists every override (model, total rows, batch size, sample size, GPU rate, …). The GPU catalogue lives in supernova/throughput.py:GPU_TABLE.
Manual back-of-envelope¶
total_tokens = rows * mean_tokens_per_row
gpu_hours = total_tokens / throughput_tok_s / 3600
cost = gpu_hours * price_per_gpu_hour
wall_time = gpu_hours / num_gpus
For a confidence interval on mean_tokens_per_row, profile a sample of the source with your model's tokenizer using supernova's parquet-level source:
from transformers import AutoTokenizer
from supernova.sources.huggingface import HuggingFaceSource
tok = AutoTokenizer.from_pretrained("Alibaba-NLP/gte-multilingual-base", trust_remote_code=True)
src = HuggingFaceSource(
dataset_name="HuggingFaceFW/finewiki",
split="train",
path_filter="en",
text_field="text",
limit=100_000,
)
lengths = [len(tok.encode(r["text"])) for r in src.stream()]
mean_tokens = sum(lengths) / len(lengths)
With a 100k sample the standard error is small enough to ignore for planning purposes.
Reference values¶
| Parameter | Value | Notes |
|---|---|---|
| Throughput (gte-multilingual-base, A10G, bfloat16) | ~50,000 tok/s | Budget estimate; benchmarks show 45K-87K depending on text length |
| Throughput (snowflake-arctic-embed-l-v2.0, A10G, bfloat16) | ~35,000 tok/s | Single GPU |
| A10G price (AWS spot, g5.xlarge) | ~$0.38/hr | |
| T4 price (GCP spot) | ~$0.18/hr | |
| OpenAI text-embedding-3-small | $0.02 / 1M tokens | API pricing |
Throughput varies with text length: short texts (~20 tok avg) achieve ~58K tok/s due to padding waste, while medium-to-long texts (~700+ tok avg) hit ~80-87K tok/s. Always use bfloat16 on modern GPUs — it's ~2× faster than float32 with no quality loss.
Example¶
finewiki (English): 1.82M rows, mean 676 tok/row:
total_tokens = 1,820,000 * 676 = 1.23B tokens
gpu_hours = 1,230,000,000 / 50,000 / 3600 = 6.8 GPU-hours
cost (AWS) = 6.8 * $0.38 = $2.60 (spot g5.xlarge)
wall_time = 6.8 / 10 GPUs = 41 min
Compare with OpenAI API:
Self-hosted GPU embedding is ~10× cheaper than OpenAI at this scale.
Padding waste¶
Transformer models pad every batch to the length of the longest sequence in that batch. nova throughput-predict --cutoff <N1> --cutoff <N2> --cutoff <N3> sweeps cutoffs and reports padding efficiency for each.
Typical findings:
- No truncation (cutoff ≥ 8192): 15-25% efficiency
- Cutoff = 1024: ~70% efficiency with ~70% of content retained
- Cutoff = 512: ~87% efficiency with ~50% of content retained
Set dense_embedder.max_tokens (or multivector_embedder.max_tokens) in your config to apply a truncation cutoff. Combine with truncate: true to drop content past the cutoff entirely; leave truncate: false (default) to split long docs into multiple records and embed each piece separately.
Caveats¶
- Throughput varies by model and GPU.
throughput_exp/throughput_bench.pymeasures the actual sustained tok/s on your hardware. - Long texts create multiple records. A 50K-token text with
max_tokens=8192becomes ~6 records (one embedding per piece) whentruncate: false. - GPU utilization matters. Add ~20% buffer for data loading, uploads, and container startup.
- Cost scales linearly, wall time doesn't. More GPUs reduces wall time but total cost stays the same.