whitphx's picture
whitphx HF Staff
Add env info into the ID
030f336
raw
history blame
8.51 kB
/**
* Benchmark ID Generator
*
* Creates human-readable, deterministic IDs from benchmark settings that:
* 1. Group results with identical configurations
* 2. Use model ID as directory structure (e.g., "Xenova/all-MiniLM-L6-v2/...")
* 3. Encode other parameters as filename
* 4. Are sortable and searchable
*/
export interface EnvironmentInfo {
// Node.js format
cpu?: {
model?: string;
cores?: number;
};
memory?: {
total?: string;
deviceMemory?: number; // Web browser format (GB)
};
gpu?: {
vendor?: string;
renderer?: string;
};
platform?: string;
arch?: string;
// Web browser format (direct fields)
cpuCores?: number;
}
export interface BenchmarkSettings {
platform: "node" | "web";
modelId: string;
task: string;
mode: "warm" | "cold";
device?: string;
dtype?: string;
batchSize?: number;
browser?: string;
headed?: boolean;
environment?: EnvironmentInfo;
}
/**
* Generate a benchmark ID path from settings
*
* Format: {task}/{modelId}/{platform}_{mode}_{device}_{dtype}_{batch}_{browser}_{headed}
*
* Examples:
* - "feature-extraction/Xenova/all-MiniLM-L6-v2/node_warm_cpu_fp32_b1"
* - "feature-extraction/Xenova/distilbert-base-uncased/web_warm_wasm_q8_b1_chromium"
* - "text-generation/meta-llama/Llama-2-7b/web_cold_webgpu_fp16_b4_firefox_headed"
*
* The path can be used to create directories and files:
* - Directory: {task}/{modelId}/
* - Filename: {platform}_{mode}_{device}_{dtype}_{batch}_{browser}_{headed}.jsonl
*/
export function generateBenchmarkId(settings: BenchmarkSettings): string {
// Task at top level
const task = settings.task;
// Model ID is preserved as-is (with slashes for directory structure)
const modelId = settings.modelId;
// Generate filename parts from other settings (excluding task since it's in the directory)
const filenameParts = generateFilenameParts(settings);
// Combine: task/modelId/filename
return `${task}/${modelId}/${filenameParts.join("_")}`;
}
/**
* Sanitize environment strings for use in filenames
*/
function sanitizeEnvString(str: string): string {
return str
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^a-z0-9\-]/g, "")
.substring(0, 20); // Limit length
}
/**
* Generate the filename parts (everything except task and model ID)
*/
function generateFilenameParts(settings: BenchmarkSettings): string[] {
const parts: string[] = [];
// 1. Platform (node/web)
parts.push(settings.platform);
// 2. Mode (warm/cold)
parts.push(settings.mode);
// 3. Device
if (settings.device) {
parts.push(settings.device);
} else if (settings.platform === "node") {
parts.push("cpu"); // default for node
} else {
parts.push("webgpu"); // default for web
}
// 4. DType (if specified)
if (settings.dtype) {
parts.push(settings.dtype);
}
// 5. Batch size (always include for consistency)
const batchSize = settings.batchSize || 1;
parts.push(`b${batchSize}`);
// 6. Browser (for web platform)
if (settings.platform === "web" && settings.browser) {
parts.push(settings.browser);
}
// 7. Headed mode (for web platform, only if true)
if (settings.platform === "web" && settings.headed) {
parts.push("headed");
}
// 8. Environment info (CPU, memory, architecture, GPU)
if (settings.environment) {
const env = settings.environment;
// CPU model (sanitized, first significant part) - Node.js only
if (env.cpu?.model) {
const cpuName = sanitizeEnvString(env.cpu.model.split(/[\s(]/)[0]);
if (cpuName) {
parts.push(cpuName);
}
}
// CPU cores (support both Node.js and web browser formats)
const cores = env.cpu?.cores || env.cpuCores;
if (cores) {
parts.push(`${cores}c`);
}
// Memory (support both Node.js and web browser formats)
if (env.memory?.total) {
// Node.js format: Parse memory string like "32.00 GB" -> "32gb"
const memMatch = env.memory.total.match(/^(\d+)/);
if (memMatch) {
parts.push(`${memMatch[1]}gb`);
}
} else if (env.memory?.deviceMemory) {
// Web browser format: deviceMemory is already in GB
parts.push(`${env.memory.deviceMemory}gb`);
}
// Architecture (Node.js only, browser uses platform like "MacIntel")
if (env.arch) {
parts.push(env.arch);
}
// GPU vendor/renderer for web (if using webgpu)
if (settings.platform === "web" && settings.device === "webgpu" && env.gpu) {
if (env.gpu.vendor) {
const gpuVendor = sanitizeEnvString(env.gpu.vendor.split(/[\s(]/)[0]);
if (gpuVendor && gpuVendor !== "google") { // Skip "Google Inc."
parts.push(`gpu-${gpuVendor}`);
}
}
}
}
return parts;
}
/**
* Generate a filesystem path for storing benchmark results
* Returns: { dir: "feature-extraction/Xenova/all-MiniLM-L6-v2", filename: "node_warm_cpu_fp32_b1.jsonl" }
*/
export function generateBenchmarkPath(settings: BenchmarkSettings): { dir: string; filename: string; fullPath: string } {
const dir = `${settings.task}/${settings.modelId}`;
const filenameParts = generateFilenameParts(settings);
const filename = `${filenameParts.join("_")}.jsonl`;
const fullPath = `${dir}/${filename}`;
return { dir, filename, fullPath };
}
/**
* Parse a benchmark ID path back into settings (best effort)
* This is useful for filtering and querying
*
* Example: "feature-extraction/Xenova/all-MiniLM-L6-v2/node_warm_cpu_fp32_b1"
*/
export function parseBenchmarkId(id: string): Partial<BenchmarkSettings> {
const settings: Partial<BenchmarkSettings> = {};
// Split into parts
const pathParts = id.split("/");
if (pathParts.length < 4) {
return settings; // Invalid ID - need at least task/org/model/filename
}
// Extract task (first part)
settings.task = pathParts[0];
// Extract model ID (everything from second part to second-to-last slash)
// Example: ["feature-extraction", "Xenova", "all-MiniLM-L6-v2", "node_warm_cpu_fp32_b1"]
// modelId should be "Xenova/all-MiniLM-L6-v2"
const lastSlashIdx = id.lastIndexOf("/");
const taskLength = settings.task.length + 1; // +1 for the slash
settings.modelId = id.substring(taskLength, lastSlashIdx);
// Extract filename parts (everything after the last slash)
const filenamePart = id.substring(lastSlashIdx + 1);
const parts = filenamePart.split("_");
if (parts.length < 3) {
return settings; // Invalid filename format
}
let idx = 0;
// Platform
if (parts[idx] === "node" || parts[idx] === "web") {
settings.platform = parts[idx] as "node" | "web";
idx++;
}
// Mode
if (idx < parts.length && (parts[idx] === "warm" || parts[idx] === "cold")) {
settings.mode = parts[idx] as "warm" | "cold";
idx++;
}
// Device (might be cpu, webgpu, wasm)
if (idx < parts.length && ["cpu", "webgpu", "wasm"].includes(parts[idx])) {
settings.device = parts[idx];
idx++;
}
// DType
if (idx < parts.length && ["fp32", "fp16", "q8", "q4", "int8", "uint8", "bnb4", "q4f16"].includes(parts[idx])) {
settings.dtype = parts[idx];
idx++;
}
// Batch size
if (idx < parts.length && parts[idx].startsWith("b")) {
const batch = parseInt(parts[idx].substring(1), 10);
if (!isNaN(batch)) {
settings.batchSize = batch;
idx++;
}
}
// Browser
if (idx < parts.length && ["chromium", "firefox", "webkit"].includes(parts[idx])) {
settings.browser = parts[idx];
idx++;
}
// Headed
if (idx < parts.length && parts[idx] === "headed") {
settings.headed = true;
idx++;
}
return settings;
}
/**
* Generate a human-readable display name from settings
*/
export function generateDisplayName(settings: BenchmarkSettings): string {
const parts: string[] = [];
// Model name
parts.push(settings.modelId);
// Task
parts.push(`(${settings.task})`);
// Platform and device
if (settings.platform === "web") {
parts.push(`[${settings.browser || "browser"}/${settings.device || "webgpu"}]`);
} else {
parts.push(`[node/${settings.device || "cpu"}]`);
}
// Mode
parts.push(settings.mode);
// DType if specified
if (settings.dtype) {
parts.push(settings.dtype);
}
// Batch size if not 1
const batchSize = settings.batchSize || 1;
if (batchSize !== 1) {
parts.push(`batch=${batchSize}`);
}
// Headed if true
if (settings.headed) {
parts.push("headed");
}
return parts.join(" ");
}