From 0e29cad5ec12cb069f858322f39358f0e99c941c Mon Sep 17 00:00:00 2001 From: Yingchi Long Date: Sun, 23 Jun 2024 20:14:34 +0800 Subject: [PATCH] spec: refactor to a dedicated directory --- src/spec/benchData.ts | 25 +++++ src/spec/index.ts | 178 ++++++++++++++++++++++++++++++ src/{spec.ts => spec/pop2.ts} | 199 +--------------------------------- 3 files changed, 205 insertions(+), 197 deletions(-) create mode 100644 src/spec/benchData.ts create mode 100644 src/spec/index.ts rename src/{spec.ts => spec/pop2.ts} (62%) diff --git a/src/spec/benchData.ts b/src/spec/benchData.ts new file mode 100644 index 0000000..da6bc30 --- /dev/null +++ b/src/spec/benchData.ts @@ -0,0 +1,25 @@ +import path from 'path'; + +export interface HaveSPECObjects { + objectNames: string[]; +} + +export interface SPECBenchData { + num: number; + name: string; + exe: string; +} + +export function benchpath(specdir: string, bench: SPECBenchData): string { + return path.join(specdir, 'benchspec', 'CPU', `${bench.num}.${bench.name}`); +} + +export function exepath(specdir: string, bench: SPECBenchData): string { + const benchmarkDir = benchpath(specdir, bench); + return path.join(benchmarkDir, 'exe', bench.exe); +} + +export function buildpath(specdir: string, bench: SPECBenchData): string { + const benchmarkDir = benchpath(specdir, bench); + return path.join(benchmarkDir, 'build'); +} diff --git a/src/spec/index.ts b/src/spec/index.ts new file mode 100644 index 0000000..99d8df9 --- /dev/null +++ b/src/spec/index.ts @@ -0,0 +1,178 @@ +import * as path from 'path'; +import * as fs from "fs"; +import { promisify } from "util"; +import { ChildProcessByStdio } from 'child_process'; +import { Readable, Writable } from 'stream'; +import { createInterface } from 'readline'; +import { projectRoot } from 'lyc/environment'; +import { SPECBenchData, benchpath, buildpath, exepath } from './benchData'; + +export{ pop2 } from './pop2'; + + +export function getEnvironment(specdir: string): NodeJS.ProcessEnv { + return { + ...process.env, + SPEC: specdir, + PATH: `${path.join(specdir, 'bin')}${path.delimiter}${process.env.PATH}`, + }; +} + +export interface SPEC { + newConfig: (name: string, content: string) => Promise; + benchpath: (bench: SPECBenchData) => string; + exepath: (bench: SPECBenchData) => string; + buildpath: (bench: SPECBenchData) => string; + getEnvironment: () => NodeJS.ProcessEnv; +} + +export function mkSPEC(specRoot: string): SPEC { + return { + newConfig: async (name: string, content: string) => { + await writeFile(path.join(specRoot, "config", name), content); + }, + benchpath: bench => benchpath(specRoot, bench), + exepath: bench => exepath(specRoot, bench), + buildpath: bench => buildpath(specRoot, bench), + getEnvironment: () => getEnvironment(specRoot), + }; +} + +export interface Bench { + benchpath: () => string; + exepath: () => string; + buildpath: () => string; + benchData: () => SPECBenchData; + spec: () => SPEC; +} + + +export function mkBench(spec: SPEC, bench: SPECBenchData): Bench { + return { + benchpath: () => spec.benchpath(bench), + exepath: () => spec.exepath(bench), + buildpath: () => spec.buildpath(bench), + benchData: () => bench, + spec: () => spec, + }; +} + +const specTemplate = fs.readFileSync(path.resolve(projectRoot, "assets", "specTemplate.cfg")).toString("utf-8"); + +const writeFile = promisify(fs.writeFile); + +export interface ConfigOptions { + gccdir: string; + optimize: string; +} + +export function renderConfig(options: ConfigOptions) { + return `# Rendered from TypeScript ${new Date().toLocaleString()}, do not edit!\n\n\n` + specTemplate + .replace("@@GCCDIR@@", options.gccdir) + .replace("@@OPTIMIZE@@", options.optimize); +} + + +export interface SPECResult { + outputFiles: { [key: string]: string[], }; +} + +/** + * Monitor SPEC2017 runcpu process in a subprocess. + */ +export async function watchSPEC>(process: T) { + const rl = createInterface({ + input: process.stdout, + terminal: false, + }); + + // Match & extract string like " format CSV -> /path/to/csv" + const formatRe = /^\s*format:\s*(\w+)\s*->\s*(.*)$/; + + return await new Promise((resolve, reject) => { + let outputFiles: { [key: string]: string[]; } = {}; + rl.on("line", line => { + let match; + if ((match = formatRe.exec(line)) != null) { + const type = match[1]; + const paths = match[2].split(',').map(path => path.trim()); + outputFiles[type] = paths; + } + // To match if the line contains: "runcpu finished + if (line.startsWith("runcpu finished")) { + resolve({ + outputFiles + }); + } + }); + process.on("error", error => reject(error)); + process.on("close", () => resolve({ + outputFiles + })); + }); +} + +export interface SPECResultsTable { + baseThreads: string, + baseRuntime: string, + baseRatio: string, + BaseSelected: string, + baseStatus: string, + peakThreads: string, + peakRuntime: string, + peakRatio: string, + peakSelected: string, + peakStatus: string, + description?: string, +} + +export function parseSPECCSVResultsTable(data: string) { + const lines = data.split("\n"); + let index = 0; + const eof = () => index >= lines.length; + + let results: { [key: string]: SPECResultsTable; } = {}; + while (!eof()) { + const line = lines[index]; + if (line.startsWith('"Full Results Table"')) { + index += 3; + // "Full Results Table" + + // Benchmark,"Base # Threads","Est. Base Run Time","Est. Base Ratio","Base Selected","Base Status","Peak # Threads","Est. Peak Run Time","Est. Peak Ratio","Peak Selected","Peak Status",Description + // 600.perlbench_s,1,65.357247,--,0,VE,,,,,,"test iteration #1" + // 602.gcc_s,,,,,NR,,,,,NR + // 605.mcf_s,,,,,NR,,,,,NR + // 620.omnetpp_s,,,,,NR,,,,,NR + // 623.xalancbmk_s,,,,,NR,,,,,NR + // 625.x264_s,,,,,NR,,,,,NR + // 631.deepsjeng_s,,,,,NR,,,,,NR + // 641.leela_s,,,,,NR,,,,,NR + // 648.exchange2_s,,,,,NR,,,,,NR + // 657.xz_s,,,,,NR,,,,,NR + while (!eof()) { + const dataLine = lines[index]; + if (dataLine.length == 0) + break; + const fields: (keyof SPECResultsTable)[] = [ + "baseThreads", + "baseRuntime", + "baseRatio", + "BaseSelected", + "baseStatus", + "peakThreads", + "peakRuntime", + "peakRatio", + "peakSelected", + "peakStatus", + "description", + ]; + const splitted = dataLine.split(","); + const entries: [keyof SPECResultsTable, string][] = splitted.slice(1).map((name, index) => [fields[index], name]); + results[splitted[0]] = (Object.fromEntries(entries) as { [K in keyof SPECResultsTable]: string }); + index++; + } + } + index++; + } + return results; +} diff --git a/src/spec.ts b/src/spec/pop2.ts similarity index 62% rename from src/spec.ts rename to src/spec/pop2.ts index 34b4d6c..c4af5cb 100644 --- a/src/spec.ts +++ b/src/spec/pop2.ts @@ -1,19 +1,6 @@ -import * as path from 'path'; -import * as fs from "fs"; -import { promisify } from "util"; -import { ChildProcessByStdio } from 'child_process'; -import { Readable, Writable } from 'stream'; -import { createInterface } from 'readline'; -import { projectRoot } from './environment'; +import { HaveSPECObjects, SPECBenchData } from "./benchData"; -export interface SPECBenchData { - objectNames: string[]; - num: number; - name: string; - exe: string; -} - -export const pop2: SPECBenchData = { +export const pop2: SPECBenchData & HaveSPECObjects = { objectNames: [ "netcdf/attr.o", "netcdf/dim.o", @@ -342,185 +329,3 @@ export const pop2: SPECBenchData = { name: "pop2_s", num: 628, }; - - -export function benchpath(specdir: string, bench: SPECBenchData): string { - return path.join(specdir, 'benchspec', 'CPU', `${bench.num}.${bench.name}`); -} - -export function exepath(specdir: string, bench: SPECBenchData): string { - const benchmarkDir = benchpath(specdir, bench); - return path.join(benchmarkDir, 'exe', bench.exe); -} - -export function buildpath(specdir: string, bench: SPECBenchData): string { - const benchmarkDir = benchpath(specdir, bench); - return path.join(benchmarkDir, 'build'); -} - -export function getEnvironment(specdir: string): NodeJS.ProcessEnv { - return { - ...process.env, - SPEC: specdir, - PATH: `${path.join(specdir, 'bin')}${path.delimiter}${process.env.PATH}`, - }; -} - -export interface SPEC { - newConfig: (name: string, content: string) => Promise; - benchpath: (bench: SPECBenchData) => string; - exepath: (bench: SPECBenchData) => string; - buildpath: (bench: SPECBenchData) => string; - getEnvironment: () => NodeJS.ProcessEnv; -} - -export function mkSPEC(specRoot: string): SPEC { - return { - newConfig: async (name: string, content: string) => { - await writeFile(path.join(specRoot, "config", name), content); - }, - benchpath: bench => benchpath(specRoot, bench), - exepath: bench => exepath(specRoot, bench), - buildpath: bench => buildpath(specRoot, bench), - getEnvironment: () => getEnvironment(specRoot), - }; -} - -export interface Bench { - benchpath: () => string; - exepath: () => string; - buildpath: () => string; - benchData: () => SPECBenchData; - spec: () => SPEC; -} - - -export function mkBench(spec: SPEC, bench: SPECBenchData): Bench { - return { - benchpath: () => spec.benchpath(bench), - exepath: () => spec.exepath(bench), - buildpath: () => spec.buildpath(bench), - benchData: () => bench, - spec: () => spec, - }; -} - -const specTemplate = fs.readFileSync(path.resolve(projectRoot, "assets", "specTemplate.cfg")).toString("utf-8"); - -const writeFile = promisify(fs.writeFile); - -export interface ConfigOptions { - gccdir: string; - optimize: string; -} - -export function renderConfig(options: ConfigOptions) { - return `# Rendered from TypeScript ${new Date().toLocaleString()}, do not edit!\n\n\n` + specTemplate - .replace("@@GCCDIR@@", options.gccdir) - .replace("@@OPTIMIZE@@", options.optimize); -} - - -export interface SPECResult { - outputFiles: { [key: string]: string[], }; -} - -/** - * Monitor SPEC2017 runcpu process in a subprocess. - */ -export async function watchSPEC>(process: T) { - const rl = createInterface({ - input: process.stdout, - terminal: false, - }); - - // Match & extract string like " format CSV -> /path/to/csv" - const formatRe = /^\s*format:\s*(\w+)\s*->\s*(.*)$/; - - return await new Promise((resolve, reject) => { - let outputFiles: { [key: string]: string[]; } = {}; - rl.on("line", line => { - let match; - if ((match = formatRe.exec(line)) != null) { - const type = match[1]; - const paths = match[2].split(',').map(path => path.trim()); - outputFiles[type] = paths; - } - // To match if the line contains: "runcpu finished - if (line.startsWith("runcpu finished")) { - resolve({ - outputFiles - }); - } - }); - process.on("error", error => reject(error)); - process.on("close", () => resolve({ - outputFiles - })); - }); -} - -export interface SPECResultsTable { - baseThreads: string, - baseRuntime: string, - baseRatio: string, - BaseSelected: string, - baseStatus: string, - peakThreads: string, - peakRuntime: string, - peakRatio: string, - peakSelected: string, - peakStatus: string, - description?: string, -} - -export function parseSPECCSVResultsTable(data: string) { - const lines = data.split("\n"); - let index = 0; - const eof = () => index >= lines.length; - - let results: { [key: string]: SPECResultsTable; } = {}; - while (!eof()) { - const line = lines[index]; - if (line.startsWith('"Full Results Table"')) { - index += 3; - // "Full Results Table" - - // Benchmark,"Base # Threads","Est. Base Run Time","Est. Base Ratio","Base Selected","Base Status","Peak # Threads","Est. Peak Run Time","Est. Peak Ratio","Peak Selected","Peak Status",Description - // 600.perlbench_s,1,65.357247,--,0,VE,,,,,,"test iteration #1" - // 602.gcc_s,,,,,NR,,,,,NR - // 605.mcf_s,,,,,NR,,,,,NR - // 620.omnetpp_s,,,,,NR,,,,,NR - // 623.xalancbmk_s,,,,,NR,,,,,NR - // 625.x264_s,,,,,NR,,,,,NR - // 631.deepsjeng_s,,,,,NR,,,,,NR - // 641.leela_s,,,,,NR,,,,,NR - // 648.exchange2_s,,,,,NR,,,,,NR - // 657.xz_s,,,,,NR,,,,,NR - while (!eof()) { - const dataLine = lines[index]; - if (dataLine.length == 0) - break; - const fields: (keyof SPECResultsTable)[] = [ - "baseThreads", - "baseRuntime", - "baseRatio", - "BaseSelected", - "baseStatus", - "peakThreads", - "peakRuntime", - "peakRatio", - "peakSelected", - "peakStatus", - "description", - ]; - const splitted = dataLine.split(","); - const entries: [keyof SPECResultsTable, string][] = splitted.slice(1).map((name, index) => [fields[index], name]); - results[splitted[0]] = (Object.fromEntries(entries) as { [K in keyof SPECResultsTable]: string }); - index++; - } - } - index++; - } - return results; -}