Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,9 @@ htmlcov/
*.so
*.dylib

# Whole-forward FPM datasets are external runtime inputs, not source assets.
**/fpm_forward_perf.parquet
**/fpm_forward_perf.metadata.json

# Rust build output
target/
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ SPDX-License-Identifier: Apache-2.0
AISimulate predicts LLM serving behavior and searches for strong deployment
configurations offline, without bringing up a GPU serving cluster.

Whole-forward FPM data is supplied at runtime rather than shipped in this
repository. Set `forward_model="fpm"` and pass `fpm_parquet_path` through the
prediction YAML, Python API, or Rust API; see the
[core API guide](docs/core-api.md#choosing-a-forward-pass-api).

AISimulate is the successor to the
[AIConfigurator (AIC)](https://github.com/ai-dynamo/aiconfigurator)
repository. It brings the complete AIC application and estimator into one
Expand Down
6 changes: 6 additions & 0 deletions crates/core/src/perfmodel/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ pub struct EngineConfig {
#[serde(default)]
pub forward_model: Option<String>,

/// Optional external FPM parquet used when `forward_model == "fpm"`.
/// The required metadata sidecar is resolved by replacing the parquet
/// extension with `.metadata.json`.
#[serde(default)]
pub fpm_parquet_path: Option<PathBuf>,

// KV
pub kv_block_size: Option<u32>,

Expand Down
10 changes: 8 additions & 2 deletions crates/core/src/perfmodel/engine/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,7 @@ impl Engine {
// engines would lazily re-parse the same parquet files on its first
// query (~0.5s per engine on data-rich systems). Mode/policy, memo
// caches, and the provenance accumulator stay per-engine.
let db = PerfDatabase::load_resolved_shared(
let db = PerfDatabase::load_resolved_shared_with_fpm(
systems_root,
&spec.engine.system_name,
spec.engine.backend.as_str(),
Expand All @@ -452,7 +452,12 @@ impl Engine {
matches!(
spec.engine.database_mode,
DatabaseMode::Empirical | DatabaseMode::Sol
) || spec.engine.tolerate_dirless_version,
) || spec.engine.tolerate_dirless_version
// An external whole-forward FPM artifact is self-contained
// for covered cells. Keep op tables lazy so no in-repository
// backend/version directory is required just to load it.
|| spec.engine.fpm_parquet_path.is_some(),
Comment thread
simone-chen marked this conversation as resolved.
Resolved
Comment thread
simone-chen marked this conversation as resolved.
Resolved
spec.engine.fpm_parquet_path.as_deref(),
)?
.with_mode(spec.engine.database_mode, transfer_policy);
Engine::build(spec, Arc::new(db))
Expand Down Expand Up @@ -1776,6 +1781,7 @@ mod tests {
backend: BackendKind::Vllm,
backend_version: Some("0.24.0".to_string()),
forward_model: None,
fpm_parquet_path: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 8,
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/engine/spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ mod tests {
backend: crate::BackendKind::Trtllm,
backend_version: Some("1.0.0rc3".into()),
forward_model: None,
fpm_parquet_path: None,
kv_block_size: Some(64),
parallel: ParallelMapping {
tp_size: 8,
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/fpm/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ fn fixture_engine_config() -> EngineConfig {
backend: BackendKind::Vllm,
backend_version: Some("0.24.0".to_string()),
forward_model: None,
fpm_parquet_path: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 8,
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/perfmodel/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,7 @@ mod tests {
backend: BackendKind::Vllm,
backend_version: Some("test-version".to_string()),
forward_model: None,
fpm_parquet_path: None,
kv_block_size: None,
parallel: ParallelMapping {
tp_size: 1,
Expand Down
67 changes: 62 additions & 5 deletions crates/core/src/perfmodel/perf_database/fpm_forward.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,12 @@
//! collector pair
//!
//! ```text
//! <data_root>/fpm_forward_perf.parquet
//! <data_root>/fpm_forward_perf.metadata.json
//! <configured path>.parquet
//! <configured path>.metadata.json
Comment thread
simone-chen marked this conversation as resolved.
Resolved
Comment thread
simone-chen marked this conversation as resolved.
Resolved
//! ```
//!
//! is validated (sidecar schema/sha256/row_count, per-row workload checks,
//! The parquet may be supplied outside the AISimulate repository and is
//! validated (sidecar schema/sha256/row_count, per-row workload checks,
//! duplicate physical row keys), healed of
//! `kv_seed_regime == "fake_fallback"` values (fabricated-KV measurements
//! replaced in memory by in-station extrapolation; see
Expand Down Expand Up @@ -186,8 +187,25 @@ impl FpmForwardTable {
/// identity, enforced against every row (a misplaced pair — e.g. an h200
/// parquet copied into a b200 tree — must fail loudly, not merge).
pub fn new(data_root: PathBuf, system: &str, backend: &str, version: &str) -> Self {
Self::from_parquet_path(
data_root.join(FPM_FORWARD_BASENAME),
system,
backend,
version,
)
}

/// Construct a table backed by an explicitly supplied parquet outside the
/// bundled systems-data tree. The adjacent `.metadata.json` sidecar keeps
/// the same atomic identity, digest, and schema validation as bundled data.
pub fn from_parquet_path(
parquet_path: PathBuf,
system: &str,
backend: &str,
version: &str,
) -> Self {
let raw = std::env::var(FPM_FAKE_FALLBACK_RAW_ENV).is_ok_and(|v| v == "1");
Self::new_with_replacement(data_root, system, backend, version, !raw)
Self::from_parquet_path_with_replacement(parquet_path, system, backend, version, !raw)
}

/// Explicit-replacement constructor: lets tests pin both semantics
Expand All @@ -198,9 +216,25 @@ impl FpmForwardTable {
backend: &str,
version: &str,
replace_fake_fallback: bool,
) -> Self {
Self::from_parquet_path_with_replacement(
data_root.join(FPM_FORWARD_BASENAME),
system,
backend,
version,
replace_fake_fallback,
)
}

fn from_parquet_path_with_replacement(
parquet_path: PathBuf,
system: &str,
backend: &str,
version: &str,
replace_fake_fallback: bool,
) -> Self {
Self {
parquet_path: data_root.join(FPM_FORWARD_BASENAME),
parquet_path,
system: system.to_string(),
backend: backend.to_string(),
version: version.to_string(),
Expand Down Expand Up @@ -1399,6 +1433,29 @@ pub(crate) mod tests {
FpmForwardTable::new_with_replacement(dir.to_path_buf(), "b200_sxm", "vllm", "0.25.1", true)
}

#[test]
fn external_parquet_path_supports_an_arbitrary_stem() {
let tmp = tempfile::tempdir().expect("tmpdir");
let canonical = write_pair(tmp.path(), &default_rows());
let external = tmp.path().join("reviewed-fpm.parquet");
std::fs::rename(&canonical, &external).expect("rename parquet");
std::fs::rename(
canonical.with_extension("metadata.json"),
external.with_extension("metadata.json"),
)
.expect("rename sidecar");

let table =
FpmForwardTable::from_parquet_path(external.clone(), "b200_sxm", "vllm", "0.25.1");

assert_eq!(table.parquet_path(), external);
assert!(
table
.select_cell(&default_identity(4), "org/model-a")
.is_ok()
);
Comment thread
simone-chen marked this conversation as resolved.
Resolved
}

fn loaded_table_raw(dir: &Path) -> FpmForwardTable {
FpmForwardTable::new_with_replacement(
dir.to_path_buf(),
Expand Down
Loading
Loading