Skip to content

Commit d259f51

Browse files
authored
Merge pull request #16 from aerospike-community/lyndon/bench-l2-setup
Add lmcache bench l2 harness for AerospikeL2Plugin
2 parents 1ed5ede + e69c704 commit d259f51

15 files changed

Lines changed: 452 additions & 0 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ htmlcov/
2424
.DS_Store
2525
.claude/
2626
benchmarks/results/
27+
benchmarks/l2/.env.local

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ docker/ scripts/
3939
| Unit | `pytest tests/unit -q` | No network |
4040
| Integration | `./scripts/start_aerospike_ce.sh` then `pytest tests/integration -q` | Live CE |
4141
| Ecosystem bench | `pip install -r benchmarks/requirements.txt` then `python benchmarks/run.py --profile smoke` | Not in CI by default |
42+
| L2 bench | `./scripts/setup_l2_bench.sh` then `./benchmarks/l2/run.sh` (LMCache `dev` + live CE) | Not in CI by default |
4243
| Micro bench | `RUN_BENCH=1 pytest benchmarks/micro --benchmark-only` | FakeClient only |
4344

4445
Pinned versions: `IMPLEMENTATION_PLAN.md` §0.2.

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ pip install -r benchmarks/requirements.txt
123123
python benchmarks/run.py --profile smoke
124124
```
125125

126+
**L2 adapter** ([`lmcache bench l2`](https://docs.lmcache.ai/cli/bench_l2.html), requires LMCache `dev`):
127+
128+
```bash
129+
./scripts/setup_l2_bench.sh
130+
# ./benchmarks/l2/run.sh # after Aerospike is up
131+
```
132+
126133
Micro (FakeClient, no server): `RUN_BENCH=1 pytest benchmarks/micro --benchmark-only`
127134

128135
See [`benchmarks/README.md`](benchmarks/README.md).

benchmarks/README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Harnesses for **lmcache-aerospike** live under `benchmarks/` and are **not** shi
77
| Harness | Runner | Use when |
88
|---|---|---|
99
| **Ecosystem** (`run.py`) | [ai-ecosystem-benchmark](https://github.com/aerospike-community/ai-ecosystem-benchmark) | Coordinated-omission-safe QPS/latency on a live Aerospike CE node |
10+
| **L2** (`l2/`) | [`lmcache bench l2`](https://docs.lmcache.ai/cli/bench_l2.html) + `AerospikeL2Plugin` | Store / lookup / load through the MP L2 adapter API (needs LMCache `dev`) |
1011
| **Micro** (`micro/`) | pytest-benchmark + FakeClient | Fast, no-server connector overhead / segment-size sweep |
1112

1213
## Ecosystem harness (recommended)
@@ -49,6 +50,19 @@ aerospike://127.0.0.1:3000/lmcache?set=bench_eco_kv&num_tokens=128&target_segmen
4950
| `smoke` | `kv_hotpath` | Laptop sanity, ~5 s per test |
5051
| `kv_chunk_smoke` | `kv_chunk` | 4 MiB target segment band |
5152

53+
## L2 harness (`lmcache bench l2`)
54+
55+
Benchmarks `AerospikeL2Plugin` via LMCache’s official L2 bench CLI (not PyPI 0.4.x).
56+
57+
```bash
58+
./scripts/setup_l2_bench.sh # LMCache dev + bench deps (once)
59+
./scripts/start_aerospike_ce.sh
60+
set -a && source .aerospike-ci.env && set +a
61+
./benchmarks/l2/run.sh # when ready
62+
```
63+
64+
See [`l2/README.md`](l2/README.md).
65+
5266
## Micro harness (no Aerospike)
5367

5468
Uses `FakeClient` from unit tests — not the ecosystem runner.

benchmarks/l2/README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# L2 adapter benchmark (`lmcache bench l2`)
2+
3+
End-to-end store / lookup / load benchmarks for [`AerospikeL2Plugin`](../../src/lmcache_aerospike/l2_plugin.py) using the same [`parse_args_to_l2_adapters_config`](https://docs.lmcache.ai/mp/configuration.html) + `create_l2_adapter` path as production MP mode.
4+
5+
Upstream docs: [lmcache bench l2](https://docs.lmcache.ai/cli/bench_l2.html).
6+
7+
## Requirements
8+
9+
| Piece | Why |
10+
| ----- | --- |
11+
| **LMCache `dev`** | PyPI `lmcache` 0.4.x does not ship `lmcache bench l2` or `L2StoreResult`. |
12+
| **This package** (`pip install -e .`) | Provides `type: plugin``AerospikeL2Plugin`. |
13+
| **torch + openai** | Bench CLI and `TensorMemoryObj` payloads (see `benchmarks/l2/requirements.txt`). |
14+
| **Aerospike CE** (for real runs) | `./scripts/start_aerospike_ce.sh``.aerospike-ci.env`. |
15+
16+
`native_storage_ops` is optional for bench: `benchmarks/l2/bootstrap.py` installs the same Python fallback used in integration tests when the extension module is missing.
17+
18+
## One-time setup
19+
20+
```bash
21+
# LMCache dev clone (default: ../LMCache next to this repo)
22+
git clone https://github.com/LMCache/LMCache.git ../LMCache
23+
cd ../LMCache && git checkout dev
24+
25+
cd /path/to/lmcache-aerospike
26+
./scripts/setup_l2_bench.sh
27+
```
28+
29+
Override the LMCache path:
30+
31+
```bash
32+
LMCACHE_SRC=/path/to/LMCache ./scripts/setup_l2_bench.sh
33+
```
34+
35+
Preflight only:
36+
37+
```bash
38+
python3 scripts/preflight_l2_bench.py
39+
```
40+
41+
## Running (when you are ready)
42+
43+
Start Aerospike and load connection env (dynamic port is written to `.aerospike-ci.env`):
44+
45+
```bash
46+
./scripts/start_aerospike_ce.sh
47+
set -a && source .aerospike-ci.env && set +a
48+
```
49+
50+
Smoke (store → lookup → load, defaults from [bench_l2 docs](https://docs.lmcache.ai/cli/bench_l2.html)):
51+
52+
```bash
53+
./benchmarks/l2/run.sh
54+
```
55+
56+
Stress profile and single operation:
57+
58+
```bash
59+
./benchmarks/l2/run.sh --profile stress
60+
./benchmarks/l2/run.sh --only store
61+
./benchmarks/l2/run.sh -- --no-skip-verify # round-trip byte check on last round
62+
```
63+
64+
Direct CLI (equivalent; `run.sh` sets `L2_ADAPTER_JSON` from `adapters/` + `.aerospike-ci.env`):
65+
66+
```bash
67+
python3 benchmarks/l2/bootstrap.py
68+
export L2_ADAPTER_JSON="$(cat benchmarks/l2/adapters/aerospike_smoke.json)"
69+
lmcache bench l2 --l2-adapter "$L2_ADAPTER_JSON" --num-keys 32 --in-flight 1
70+
```
71+
72+
## Layout
73+
74+
| Path | Purpose |
75+
| ---- | ------- |
76+
| `adapters/*.json` | Plugin specs (`type: plugin`, `AerospikeL2Plugin`). Hosts/port overridden by `run.sh`. |
77+
| `profiles/*.env` | Default CLI flag bundles (`BENCH_L2_EXTRA_ARGS`). |
78+
| `bootstrap.py` | `native_storage_ops` fallback + `init_remote_metadata_info(1)`. |
79+
| `run.sh` | Sources Aerospike env, renders adapter JSON, runs `lmcache bench l2`. |
80+
| `env.example` | Optional `benchmarks/l2/.env.local` knobs. |
81+
82+
Bench records use set **`kv_chunks_bench_l2`** by default (not `it_chunks` or `kv_chunks_l2_it`).
83+
84+
## Cold-cache / isolated operations
85+
86+
For adapters backed by the OS page cache, run operations separately and drop caches between runs (see upstream note in [bench_l2](https://docs.lmcache.ai/cli/bench_l2.html)):
87+
88+
```bash
89+
./benchmarks/l2/run.sh --only store
90+
sync && echo 3 | sudo tee /proc/sys/vm/drop_caches
91+
./benchmarks/l2/run.sh --only lookup
92+
```
93+
94+
Aerospike has no local page cache; the default combined `run.sh` pass is usually representative.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"type": "plugin",
3+
"module_path": "lmcache_aerospike.l2_plugin",
4+
"class_name": "AerospikeL2Plugin",
5+
"adapter_params": {
6+
"hosts": "127.0.0.1:3000",
7+
"namespace": "lmcache",
8+
"set": "kv_chunks_bench_l2"
9+
}
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"type": "plugin",
3+
"module_path": "lmcache_aerospike.l2_plugin",
4+
"class_name": "AerospikeL2Plugin",
5+
"adapter_params": {
6+
"hosts": "127.0.0.1:3000",
7+
"namespace": "lmcache",
8+
"set": "kv_chunks_bench_l2",
9+
"default_ttl_seconds": 86400
10+
}
11+
}

benchmarks/l2/bootstrap.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""Runtime bootstrap for ``lmcache bench l2`` against AerospikeL2Plugin."""
2+
3+
from __future__ import annotations
4+
5+
import importlib.util
6+
import os
7+
import sys
8+
from pathlib import Path
9+
10+
11+
def _repo_root() -> Path:
12+
return Path(__file__).resolve().parents[2]
13+
14+
15+
def _lmcache_src() -> Path:
16+
env = os.environ.get("LMCACHE_SRC")
17+
if env:
18+
return Path(env).expanduser().resolve()
19+
return _repo_root().parent / "LMCache"
20+
21+
22+
def install_native_storage_ops_fallback() -> None:
23+
"""Use LMCache test fallback when ``lmcache.native_storage_ops`` is incomplete."""
24+
try:
25+
native = __import__("lmcache.native_storage_ops", fromlist=["Bitmap"])
26+
if hasattr(native, "Bitmap") and hasattr(native, "TTLLock"):
27+
return
28+
except Exception:
29+
pass
30+
31+
utils = _lmcache_src() / "tests/v1/storage_backend/raw_block_test_utils.py"
32+
if not utils.is_file():
33+
raise RuntimeError(
34+
"lmcache.native_storage_ops is unavailable and LMCache test utils were not found.\n"
35+
f" Expected: {utils}\n"
36+
" Set LMCACHE_SRC to your LMCache dev clone, or build LMCache with native extensions."
37+
)
38+
spec = importlib.util.spec_from_file_location("lmcache_raw_block_test_utils", utils)
39+
if spec is None or spec.loader is None:
40+
raise RuntimeError(f"Could not load LMCache test utils from {utils}")
41+
mod = importlib.util.module_from_spec(spec)
42+
spec.loader.exec_module(mod)
43+
mod.install_native_storage_ops_fallback()
44+
45+
46+
def bootstrap() -> None:
47+
install_native_storage_ops_fallback()
48+
from lmcache.v1.protocol import init_remote_metadata_info
49+
50+
init_remote_metadata_info(1)
51+
52+
53+
def main() -> None:
54+
bootstrap()
55+
print("OK: L2 bench bootstrap")
56+
57+
58+
if __name__ == "__main__":
59+
try:
60+
main()
61+
except Exception as exc:
62+
print(exc, file=sys.stderr)
63+
sys.exit(1)

benchmarks/l2/env.example

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Copy to benchmarks/l2/.env.local (optional) or export in your shell.
2+
# After ./scripts/start_aerospike_ce.sh, prefer sourcing the repo root file:
3+
# set -a && source .aerospike-ci.env && set +a
4+
5+
# Aerospike (overrides defaults in adapters/*.json when using benchmarks/l2/run.sh)
6+
# AEROSPIKE_TEST_HOST=127.0.0.1
7+
# AEROSPIKE_TEST_PORT=3000
8+
# AEROSPIKE_TEST_NAMESPACE=lmcache
9+
# Dedicated set so bench data does not collide with integration tests (it_chunks, kv_chunks_l2_it).
10+
AEROSPIKE_BENCH_L2_SET=kv_chunks_bench_l2
11+
12+
# Optional: pass adapter JSON directly instead of --adapter aerospike_smoke.json
13+
# L2_ADAPTER_JSON='{"type":"plugin","module_path":"lmcache_aerospike.l2_plugin",...}'
14+
15+
# LMCache source for scripts/setup_l2_bench.sh (default: sibling ../LMCache)
16+
# LMCACHE_SRC=/path/to/LMCache

benchmarks/l2/profiles/smoke.env

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Default CLI flags for a quick store -> lookup -> load pass (see docs.lmcache.ai/cli/bench_l2.html).
2+
BENCH_L2_EXTRA_ARGS="--num-keys 32 --in-flight 1 --data-size-kb 256 --rounds 1 --warmup-rounds 1"

0 commit comments

Comments
 (0)