spec: refactor to a dedicated directory

This commit is contained in:
2024-06-23 20:14:34 +08:00
parent 505c5ef752
commit 0e29cad5ec
3 changed files with 205 additions and 197 deletions

25
src/spec/benchData.ts Normal file
View File

@@ -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');
}

178
src/spec/index.ts Normal file
View File

@@ -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<void>;
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<T extends ChildProcessByStdio<null | Writable, Readable, Readable>>(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<SPECResult>((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;
}

View File

@@ -1,19 +1,6 @@
import * as path from 'path'; import { HaveSPECObjects, SPECBenchData } from "./benchData";
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';
export interface SPECBenchData { export const pop2: SPECBenchData & HaveSPECObjects = {
objectNames: string[];
num: number;
name: string;
exe: string;
}
export const pop2: SPECBenchData = {
objectNames: [ objectNames: [
"netcdf/attr.o", "netcdf/attr.o",
"netcdf/dim.o", "netcdf/dim.o",
@@ -342,185 +329,3 @@ export const pop2: SPECBenchData = {
name: "pop2_s", name: "pop2_s",
num: 628, 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<void>;
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<T extends ChildProcessByStdio<null | Writable, Readable, Readable>>(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<SPECResult>((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;
}