perf(dashboard): 仪表盘加载提速
- models-dev 快照移出主 bundle(1.35MB→515KB),改为 /models-dev.json 运行时异步加载,就绪后重渲染 - Rust 启动时后台预热价格索引(warm_price_index),首次 get_summary 不再卡 - get_summary 扫描结果 8s TTL 缓存(按 home),切 tab/改范围复用;刷新按钮 refresh=true 强制重扫
This commit is contained in:
1 parent
9fd22ef55a
commit
1fecb161a9
7 files changed
+64112
-23
No files matched your search
@@ -530,6 +530,15 @@ fn models_dev_cost_index() -> &'static HashMap<String, ModelsDevCost> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Warm the compiled-in models.dev price index off the first dashboard open:
|
||||
/// parsing the ~1.3 MB snapshot costs tens of milliseconds, so build it on a
|
||||
/// background thread at startup instead of lazily on the first get_summary.
|
||||
pub fn warm_price_index() {
|
||||
std::thread::spawn(|| {
|
||||
let _ = models_dev_cost_index();
|
||||
});
|
||||
}
|
||||
|
||||
/// Official providers take precedence over resellers when the same model id
|
||||
/// ships under multiple providers and no provider prefix disambiguates.
|
||||
const OFFICIAL_PROVIDERS: &[&str] = &[
|
||||
@@ -743,6 +752,45 @@ fn scan_usage(home: &Path) -> (Vec<UsageRecord>, ScanMeta) {
|
||||
})
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Scan cache
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Short-TTL cache for the expensive `scan_usage` walk (reads every
|
||||
/// wire.jsonl under the sessions root and prices every record). Tab switches
|
||||
/// call get_summary repeatedly; re-walking the whole tree on every switch is
|
||||
/// the main dashboard lag. Keyed by home, expires after 8s; `refresh=true`
|
||||
/// bypasses it.
|
||||
struct SummaryCacheEntry {
|
||||
home: String,
|
||||
scanned_at: std::time::Instant,
|
||||
records: Vec<UsageRecord>,
|
||||
meta: ScanMeta,
|
||||
}
|
||||
|
||||
static SUMMARY_CACHE: Mutex<Option<SummaryCacheEntry>> = Mutex::new(None);
|
||||
const SUMMARY_CACHE_TTL: std::time::Duration = std::time::Duration::from_secs(8);
|
||||
|
||||
fn scan_usage_cached(home: &Path, refresh: bool) -> (Vec<UsageRecord>, ScanMeta) {
|
||||
let home_s = home.to_string_lossy().to_string();
|
||||
let mut cache = SUMMARY_CACHE.lock().unwrap();
|
||||
let hit = cache
|
||||
.as_ref()
|
||||
.is_some_and(|e| !refresh && e.home == home_s && e.scanned_at.elapsed() < SUMMARY_CACHE_TTL);
|
||||
if hit {
|
||||
let e = cache.as_ref().unwrap();
|
||||
return (e.records.clone(), e.meta.clone());
|
||||
}
|
||||
let (records, meta) = scan_usage(home);
|
||||
*cache = Some(SummaryCacheEntry {
|
||||
home: home_s,
|
||||
scanned_at: std::time::Instant::now(),
|
||||
records: records.clone(),
|
||||
meta: meta.clone(),
|
||||
});
|
||||
(records, meta)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Aggregate
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -1489,13 +1537,13 @@ pub fn get_prices() -> PricesResult {
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_summary(home_override: Option<String>, range: Option<String>, refresh: Option<bool>) -> SummaryResult {
|
||||
let _refresh = refresh.unwrap_or(false);
|
||||
let refresh = refresh.unwrap_or(false);
|
||||
let home = resolve_kimi_home(home_override);
|
||||
let now_ms = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH).unwrap_or_default().as_millis() as u64;
|
||||
let r = range.unwrap_or_else(|| "30d".into());
|
||||
|
||||
let (records, meta) = scan_usage(&home);
|
||||
let (records, meta) = scan_usage_cached(&home, refresh);
|
||||
let stats = aggregate(&records, &r, now_ms);
|
||||
let all_stats = aggregate(&records, "all", now_ms);
|
||||
let heatmap = build_heatmap(&records, now_ms);
|
||||
|
||||
@@ -27,6 +27,8 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.setup(|app| {
|
||||
println!("[Tauri] Setup started");
|
||||
// Build the models.dev price index off the first dashboard query.
|
||||
dashboard::warm_price_index();
|
||||
let window = app.get_webview_window("main").unwrap();
|
||||
println!("[Tauri] Window label: {}", window.label());
|
||||
|
||||
|
||||
Reference in new issue
Block a user