treewide: rename to src/

This commit is contained in:
2024-06-20 15:16:49 +08:00
parent d396b0d24d
commit e9fd3de6c5
15 changed files with 60 additions and 14 deletions

25
src/algorithm.ts Normal file
View File

@@ -0,0 +1,25 @@
/**
* Performs a binary search within a specified range.
* @param begin The starting index of the search range.
* @param end The ending index of the search range.
* @param beforeHalf A list of predicates indicating conditions before the midpoint.
* @returns A tuple containing the indices representing the binary search state: [begin, half, end].
*/
export function binSearch(begin: number, end: number, beforeHalf: boolean[]): [number, number, number] {
const gHalf = () => { return Math.floor((end - begin) / 2) + begin; };
for (const pred of beforeHalf) {
const half = gHalf();
if (pred) {
// Happens before "half"
end = half;
} else {
begin = half;
}
}
const half = gHalf();
console.log(`Checking range: [${begin}, ${end}), ${end - begin} objects, mid ${half}`);
return [begin, half, end];
}

5
src/build-llvm.ts Normal file
View File

@@ -0,0 +1,5 @@
import { LLVM_SRC } from "./environment";
import { buildLLVMHash } from "./build/llvmPackages";
buildLLVMHash(LLVM_SRC, "e201a0b21b8933b7df79d5cfca2c1e89348aad1f");

27
src/build/build.ts Normal file
View File

@@ -0,0 +1,27 @@
export interface PackageTask {
name: string;
patchPhase?: (task: PackageTask) => void;
configurePhase?: (task: PackageTask) => void;
buildPhase?: (task: PackageTask) => void;
installPhase?: (task: PackageTask) => void;
[key: string]: any;
}
export function buildPackage(task: PackageTask) {
if (task.patchPhase) {
console.log(`${task.name}/patchPhase`);
task.patchPhase(task);
}
if (task.configurePhase) {
console.log(`${task.name}/configurePhase`);
task.configurePhase(task);
}
if (task.buildPhase) {
console.log(`${task.name}/buildPhase`);
task.buildPhase(task);
}
if (task.installPhase) {
console.log(`${task.name}/installPhase`);
task.installPhase(task);
}
}

154
src/build/llvmPackages.ts Normal file
View File

@@ -0,0 +1,154 @@
import path from "path";
import { GeneralVariable, LLVMVariable, command, variable } from "lyc/commands/cmake";
import { SYSROOT_PREFIX } from "lyc/environment";
import { PackageTask, buildPackage } from "./build";
import { checkedSpawnSync } from "lyc/cli";
import os from "os";
import { SpawnOptions, spawn } from "child_process";
export function llvmPackages(src: string, installPrefix: string): PackageTask[] {
const LLVM_BUILD = path.join(src, "build-dev");
const rm = "rm";
const cmake = "cmake";
const llvmVar: LLVMVariable = {
LLVM_ENABLE_PROJECTS: ["clang", "clang-tools-extra", "lld", "openmp"],
LLVM_TARGETS_TO_BUILD: ["Sw64"],
LIBOMP_ARCH: "Sw64",
LIBOMP_USE_ITT_NOTIFY: false,
};
const general: GeneralVariable = {
CMAKE_BUILD_TYPE: "Release",
CMAKE_INSTALL_PREFIX: installPrefix,
CMAKE_SYSROOT: SYSROOT_PREFIX,
};
const env = {
...process.env,
MAKEFLAGS: ["-j", os.cpus().length].join(" "),
LD_LIBRARY_PATH: [
path.join(SYSROOT_PREFIX, 'usr', 'lib'),
path.join(SYSROOT_PREFIX, 'lib'),
].join(':'),
};
const spawnOptions: SpawnOptions = {
stdio: "inherit",
env,
cwd: src,
};
const mkBuildPhase = (build: string) => {
checkedSpawnSync(cmake, ["--build", build], spawnOptions);
};
const mkInstallPhase = (build: string) => {
checkedSpawnSync(cmake, ["--build", build, "--target", "install"], spawnOptions);
};
function mkLLVMPackage(): PackageTask {
const build = path.join(LLVM_BUILD, "llvm");
return {
name: "llvm",
patchPhase: () => {
// Apply llvm omp patch
checkedSpawnSync("git",
[
"apply",
path.resolve(__dirname, "..", "..", "assets", "omp.diff"),
], spawnOptions);
},
configurePhase: () => {
// Configure
checkedSpawnSync(cmake, command({
definitions: variable({
...llvmVar,
...general,
}),
generator: "Ninja",
// LLVM cmake root is under /llvm directory
pathToSource: path.join(src, "llvm"),
pathToBuild: build,
}), spawnOptions);
},
buildPhase: () => mkBuildPhase(build),
installPhase: () => mkInstallPhase(build),
};
}
function mkLibpgmathPackage(): PackageTask {
const build = path.join(LLVM_BUILD, "libpgmath");
return {
name: "libpgmath",
configurePhase: () => {
checkedSpawnSync(rm, ["-rf", build], spawnOptions);
// Configure libpgmath.
checkedSpawnSync(cmake, command({
definitions: variable({
...general,
...{
CMAKE_C_FLAGS: "-msimd",
CMAKE_CXX_FLAGS: "-msimd",
CMAKE_C_COMPILER: path.join(installPrefix, "bin", "clang"),
CMAKE_CXX_COMPILER: path.join(installPrefix, "bin", "clang++"),
} as GeneralVariable,
}),
generator: "Ninja",
pathToSource: path.join(src, "cflang", "runtime", "libpgmath"),
pathToBuild: build,
}), spawnOptions);
},
buildPhase: () => mkBuildPhase(build),
installPhase: () => mkInstallPhase(build),
};
}
function mkCflangPackage(): PackageTask {
const build = path.join(LLVM_BUILD, "cflang");
return {
name: "cflang",
configurePhase: () => {
checkedSpawnSync(rm, ["-rf", build], spawnOptions);
// Configure cflang.
checkedSpawnSync(cmake, command({
definitions: variable({
...llvmVar,
...general,
...{
CMAKE_C_COMPILER: path.join(installPrefix, "bin", "clang"),
CMAKE_CXX_COMPILER: path.join(installPrefix, "bin", "clang++"),
CMAKE_Fortran_COMPILER: path.join(installPrefix, "bin", "flang"),
CMAKE_Fortran_COMPILER_ID: "Flang",
} as GeneralVariable,
// Flang specific variables.
FLANG_INCLUDE_DOCS: true,
FLANG_LLVM_EXTENSIONS: true,
WITH_WERROR: false,
LLVM_CONFIG: path.join(installPrefix, "bin", "llvm-config"),
}),
generator: "Unix Makefiles",
pathToSource: path.join(src, "cflang"),
pathToBuild: build,
}), spawnOptions);
},
buildPhase: () => mkBuildPhase(build),
installPhase: () => mkInstallPhase(build),
};
}
return [mkLLVMPackage(), mkLibpgmathPackage(), mkCflangPackage()];
} export function buildLLVMHash(src: string, rev: string) {
// Checkout a git rev in some git repo.
checkedSpawnSync("git", ["checkout", rev], { stdio: "inherit", cwd: src });
checkedSpawnSync("git", ["reset", "--hard", "HEAD"], { stdio: "inherit", cwd: src });
const llvmInstall = path.join(src, "local", "installed");
llvmPackages(src, path.join(llvmInstall, rev))
.forEach(buildPackage);
}

28
src/cli.ts Normal file
View File

@@ -0,0 +1,28 @@
import { ChildProcess, SpawnSyncOptionsWithBufferEncoding, SpawnSyncReturns, spawnSync as rawSpawnSync } from "child_process";
export function check(result: SpawnSyncReturns<Buffer | string>) {
if (result.error)
throw result.error;
if (result.status !== 0) {
throw result.stderr;
}
return result;
}
export function checkedSpawnSync(
command: string,
args: readonly string[],
options: SpawnSyncOptionsWithBufferEncoding,
): SpawnSyncReturns<Buffer | string> {
return check(rawSpawnSync(command, args, options));
}
export function promisifySpawn<T extends ChildProcess>(p: T) {
return new Promise<T>((resolve, reject) => {
p.on("error", err => reject(err));
p.on("close", number => {
if (number != 0) return reject(number);
resolve(p);
});
});
}

131
src/commands/cmake.ts Normal file
View File

@@ -0,0 +1,131 @@
import { optFlag, undefList } from "./common"
export type CMakeDefinition = {
name: string,
value: string,
type?: CMakeVariableType
}
export type CMakeVariableType = "BOOL" | "FILEPATH" | "PATH" | "STRING" | "INTERNAL"
/**
* General CMake variables.
*/
export interface GeneralVariable {
CMAKE_BUILD_TYPE?: "Debug" | "Release" | "RelWithDebInfo" | "MinSizeRel"
BUILD_SHARED_LIBS?: boolean,
CMAKE_INSTALL_PREFIX?: string,
CMAKE_SYSROOT?: string,
CMAKE_C_COMPILER?: string,
CMAKE_CXX_COMPILER?: string,
CMAKE_Fortran_COMPILER?: string,
CMAKE_Fortran_COMPILER_ID?: string,
CMAKE_C_FLAGS?: string,
CMAKE_CXX_FLAGS?: string,
}
/**
* CMake variables suitable for LLVM project.
*/
export interface LLVMVariable {
LLVM_TARGETS_TO_BUILD?: string[]
LLVM_ENABLE_CLASSIC_FLANG?: boolean,
LLVM_ENABLE_PROJECTS?: ("clang" | "openmp" | "lld" | "clang-tools-extra")[]
LIBOMP_ARCH?: "Sw64"
LIBOMP_USE_ITT_NOTIFY?: boolean
LIBOMP_ENABLE_SHARED?: boolean
OPENMP_ENABLE_LIBOMPTARGET?: boolean
}
export interface CFlangCMakeVariable {
FLANG_INCLUDE_DIRS?: boolean
FLANG_LLVM_EXTENSIONS?: boolean
WITH_WERROR?: boolean
LLVM_CONFIG?: string
}
/**
* Generate cmake definitions, take care of funny cmake string lists.
*
* @param object A set of cmake variables to generate
* @returns A list of cmake definitions, could be passed to CLI
*/
export function variable(object: Object): CMakeDefinition[] {
return Object.entries(object).map(([k, v]) => {
if (typeof v === "boolean") {
return {
name: k,
value: v ? "ON" : "OFF",
type: "BOOL"
}
} else if (typeof v === "object" && Array.isArray(v)) {
return {
name: k,
value: v.join(";"),
type: "STRING"
}
} else {
return {
name: k,
value: v,
type: "STRING"
}
}
}
)
}
/**
* Options for "Generate a Project Buildsystem"
* @see https://cmake.org/cmake/help/latest/manual/cmake.1.html#generate-a-project-buildsystem
*/
export interface CMakeGenerateOptions {
pathToBuild?: string
pathToSource?: string
/**
* Pre-load a script to populate the cache.
* When CMake is first run in an empty build tree, it creates a
* CMakeCache.txt file and populates it with customizable settings for the project.
* This option may be used to specify a file from which to load cache entries
* before the first pass through the project's CMake listfiles.
* The loaded entries take priority over the project's default values.
* The given file should be a CMake script containing set() commands that use the CACHE option,
* not a cache-format file.
*/
preloadCache?: string
definitions?: CMakeDefinition[]
/**
* Specify a build system generator.
*
* CMake may support multiple native build systems on certain platforms.
* A generator is responsible for generating a particular build system.
* Possible generator names are specified in the [cmake-generators(7)](https://cmake.org/cmake/help/latest/manual/cmake-generators.7.html#manual:cmake-generators(7)) manual.
*/
generator?: "Unix Makefiles" | "Ninja"
/**
* Specify the cross compiling toolchain file, equivalent to setting CMAKE_TOOLCHAIN_FILE variable.
* Relative paths are interpreted as relative to the build directory,
* and if not found, relative to the source directory.
*
* New in version 3.21.
*/
toolchain?: string
}
export function command(o: CMakeGenerateOptions): string[] {
return [
...optFlag("-G", o.generator),
...undefList(o.definitions, defs => defs.flatMap(def => [
"-D",
`${def.name}${def.type ? `:${def.type}` : ""}=${def.value}`
])),
...optFlag("-B", o.pathToBuild),
...optFlag("-S", o.pathToSource),
]
}

15
src/commands/common.ts Normal file
View File

@@ -0,0 +1,15 @@
export function undefList<T, U>(opt: T | undefined, fn: (opt: T) => U) {
return opt === undefined ? [] : fn(opt)
}
export const optFlag = (flag: string, opt: string | undefined) => undefList(opt, opt => [flag, opt])
/**
* Generate a switch flag, like "--rebuild", "--nobuild"
*/
export const optSwitch = (flag: string, opt: boolean | undefined) => undefList(opt, opt => opt ? [flag] : [])
export const optMultiFlag = (flag: string, opt: string[] | undefined) =>
undefList(
opt,
opt => opt.flatMap(name => [flag, name]))

93
src/commands/compiler.ts Normal file
View File

@@ -0,0 +1,93 @@
import { optFlag, optMultiFlag, optSwitch, undefList } from "./common";
export interface GeneralOptions {
output?: string
sysroot?: string
outputKind?: "exe" | "object" | "assembly" | "preprocessed"
}
export function generalCommand(options: GeneralOptions) {
return [
...optFlag("-o", options.output),
...undefList(options.outputKind, (opt) => {
switch (opt) {
case "object":
return ["-c"]
case "exe":
return []
case "assembly":
return ["-S"]
case "preprocessed":
return ["-E"]
}
}),
...optFlag("--sysroot", options.sysroot),
]
}
interface LinkerOptions {
/**
* Directory searched for "-l"
*/
searchDirs?: string[];
/**
* -l libraries.
* Static library dependecies must be sorted in topologic order.
*/
libraries?: string[];
}
export function linkerCommand(options: LinkerOptions) {
return [
...optMultiFlag("-L", options.searchDirs),
...optMultiFlag("-l", options.libraries),
]
}
export interface PreprocessorOptions {
includeDirs?: string[];
}
export function preprocessorCommand(options: PreprocessorOptions) {
return [
...optMultiFlag("-I", options.includeDirs),
]
}
/**
* CLI options used for llvm-extract executable.
*/
export interface ExtractOptions {
/**
* Functions to extract.
*/
func?: string[];
/**
* Basic block specifiers.
*/
bb?: string[];
output?: string;
/**
* Input file name. If unspecified, llvm-extract reads from stdin
*/
input?: string;
asm?: boolean;
}
export function extractCommand(options: ExtractOptions): string[] {
return [
...optMultiFlag("--func", options.func),
...optMultiFlag("--bb", options.bb),
...optFlag("-o", options.output),
...optSwitch("-S", options.asm),
...undefList(options.input, input => [input])
];
}

71
src/commands/spec.ts Normal file
View File

@@ -0,0 +1,71 @@
import { optFlag, undefList } from "./common"
export interface RunCPUOptions {
/**
* Config file, used for compiling flags, compiler versions, etc.
*/
config?: string
/**
* SPEC workload scale for each benchmark
*/
workload?: "test" | "train" | "ref"
/**
* Selected benchmarks
*/
benchmarks?: string[]
buildType?: "nobuild" | "rebuild" | "plain",
/**
* Default: HTML and text
* Meaning: Desired report format.
* An output format may be deselected by prepending any of the names with the word "no".
* If more than one option is used, separate them by commas or colons.
* The values are not case-sensitive.
*/
outputFormat?: ("all"
/** config file used for this run, written as a numbered file in the result directory, for example */
| "config"
/** Reportable syntax check */
| "check"
/**
* Comma-separated variable.
* If you populate spreadsheets from your runs,
* you probably should not cut/paste data from text files;
* you'll get more accurate data by using --output_format csv.
* The csv report includes all runs, more decimal places,
* system information, and even the compiler flags.
*/
| "csv"
| "default"
| "flags"
| "html"
| "mail"
| "pdf"
| "postscript"
| "raw"
| "screen"
/** Plain ASCII text file */
| "text")[];
}
export function runcpuOptions(o: RunCPUOptions): string[] {
return [
...optFlag("-c", o.config),
...optFlag("-i", o.workload),
...undefList(o.buildType, opt => {
switch (opt) {
case "nobuild":
return ["--nobuild"]
case "rebuild":
return ["--rebuild"]
case "plain":
return []
}
}),
...undefList(o.benchmarks, bench => bench),
...undefList(o.outputFormat, of => ["--output_format", of.join(",")]),
]
}

31
src/commands/systemd.ts Normal file
View File

@@ -0,0 +1,31 @@
import { optFlag, optSwitch } from "./common";
export interface SystemdRunOptions {
/**
* Create a transient .scope unit instead of the default transient .service unit (see above).
*
* @since 206
*/
scope?: boolean;
/**
* Use this unit name instead of an automatically generated one.
*
* @since 206
*/
unit?: string;
/**
* Talk to the service manager of the calling user, rather than the service manager of the system.
*/
user?: boolean;
}
export function systemdRunOptions(o: SystemdRunOptions): string[] {
return [
...optSwitch("--scope", o.scope),
...optFlag("--unit", o.unit),
...optSwitch("--user", o.user),
];
}

19
src/environment.ts Normal file
View File

@@ -0,0 +1,19 @@
import path from "path"
export const HOME = path.join("/home", "lyc")
export const SW_AUTOVEC = path.join(HOME, "workspace", "sw-autovec")
export const LLVM_SRC = path.join(SW_AUTOVEC, "swllvm-13.0.0-vect0919")
export const LLVM_INSTALL = path.join(LLVM_SRC, "local", "installed")
/// Default toolchain version used in SPEC2017
export const PREFIX = path.join(LLVM_INSTALL, "85e4fed0c9e4cd4ab8bce89f307127ccbad31294")
export const PREFIXBIN = path.join(PREFIX, "bin")
export const LLVM_EXTRACT = path.join(PREFIXBIN, "llvm-extract")
export const CXX = path.join(PREFIXBIN, "clang++")
export const CC = path.join(PREFIXBIN, "clang")
export const FLANG = path.join(PREFIXBIN, "flang")
export const SYSROOT_PREFIX = "/usr/sw/standard-830-6b-test"

37
src/gua-spec.ts Normal file
View File

@@ -0,0 +1,37 @@
import { spawn } from "child_process";
import { promisifySpawn } from "lyc/cli";
import { runcpuOptions } from "lyc/commands/spec";
import { systemdRunOptions } from "lyc/commands/systemd";
import { LLVM_INSTALL } from "lyc/environment";
import path from "path";
import { defaultSPEC, renderConfig } from "lyc/spec";
(async () => {
const llvmHash = "31c8e21f40ea654f9d49b3a926acc6ef1f2ca5d5"; // ipa-pure-const.c
const llvmPrefix = path.join(LLVM_INSTALL, llvmHash);
const config = `clang-O2-${llvmHash}.cfg`;
const spec = defaultSPEC;
spec.newConfig(config, renderConfig({
gccdir: llvmPrefix,
optimize: ["-O2", "-msimd"].join(" "),
}));
await promisifySpawn(spawn("systemd-run", [
...systemdRunOptions({
scope: false,
unit: "spec-loadu",
user: true,
}),
"runcpu",
...runcpuOptions({
benchmarks: ["intspeed"],
buildType: "rebuild",
config,
workload: "ref",
})
], { env: spec.getEnvironment(), }));
})();

271
src/mix-object.ts Normal file
View File

@@ -0,0 +1,271 @@
/**
* Binary search the first bad object in object sets.
* Things todo:
* 1. Determine the set of vector, or scalar version.
* 2. Use the mechanism above, construct such set
* 3. Link the set to an executable -> SPEC path, invoke "runcpu"
*/
import { binSearch } from "./algorithm";
import { pop2, SPEC, Bench, mkBench, mkSPEC } from "./spec";
import path from "path";
import { FLANG, PREFIX, SW_AUTOVEC, SYSROOT_PREFIX } from "./environment";
import { generalCommand, linkerCommand } from "./commands/compiler";
import { runcpuOptions } from "./commands/spec";
import { checkedSpawnSync, promisifySpawn } from "./cli";
import { systemdRunOptions } from "./commands/systemd";
import { spawn } from "child_process";
export interface Linkable {
link: (objectPath: (objectNames: string[]) => string[]) => Promise<void>;
}
function mkPop2Bench(spec: SPEC): Bench & Linkable {
const base = mkBench(spec, pop2);
return {
...base,
async link(objectPath) {
// Set up environment variables.
// FIXME: use -rpath or -rpath-link after I find out why.
const objects = objectPath(base.benchData().objectNames);
const options = [
...objects,
...linkerCommand({
searchDirs: [path.join(PREFIX, "lib"),],
}),
...generalCommand({
output: base.exepath(),
outputKind: "exe",
sysroot: SYSROOT_PREFIX,
}),
];
// Execute the link command
const proc = checkedSpawnSync(FLANG, options, {
stdio: "inherit",
env: {
...process.env,
LD_LIBRARY_PATH: [
path.join(PREFIX, 'lib'),
path.join(SYSROOT_PREFIX, 'lib'),
path.join(SYSROOT_PREFIX, 'usr', 'lib')
].join(':')
},
});
if (proc.error) {
throw proc.error;
}
},
};
}
// Bisect results.
const beforeHalfGrid = [
// 0.336
// Scalar [0, 161): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.671.fpspeed.refspeed.txt
// : /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.664.fpspeed.refspeed.txt
// Scalar [161, 322): Correct.
false,
// Scalar [161, 241): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.672.fpspeed.refspeed.txt
// Scalar [241, 322): Error,
// /home/lyc/workspace/sw-autovec/spec2017-2/result/CPU2017.672.fpspeed.refspeed.rsf
// Vector [161, 241): Error,
// Vector [241, 322): Error,
// /home/lyc/workspace/sw-autovec/spec2017-2/result/CPU2017.673.fpspeed.refspeed.txt
false,
// Vector [241, 281): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.674.fpspeed.refspeed.txt
// Vector [281, 322): Correct.
true,
// Vector [241, 261): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.675.fpspeed.refspeed.rsf
// Vector [261, 281): Correct.
true,
// Vector [241, 251): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.676.fpspeed.refspeed.txt
// Vector [251, 261): Stopped.
true,
// Vector [241, 246): Error,
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.677.fpspeed.refspeed.rsf
];
const beforeHalfNoGrid: boolean[] = [
false,
// Vector [161, 241): Error
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.679.
// Vector [241, 322): Unknown
true,
// Vector [161, 201): Unknown
// Vector [201, 241): Error
false,
// Vector [201, 221): Error
// /home/lyc/workspace/sw-autovec/spec2017/result/CPU2017.681
// Vector [221, 241): Unknown
true,
// Vector [201, 211): Error
// Vector [211, 221): Unknown
true,
// Vector [201, 206): Unknown
// Vector [206, 211): Error
false,
// Vector [206, 208): Error
// Vector [208, 211): ?
true,
// Vector [206, 207): ?
// !!! Vector [207, 208): Error
];
async function bisect(
beforeHalf: boolean[],
/** specify which spec to use */
rangeSPEC: (range: [number, number], index: number) => SPEC,
/** How to get the vector object path */
vector: (name: string) => string,
/** And how to get scalar path. */
scalar: (name: string) => string,
) {
const [vBegin, vHalf, vEnd] = binSearch(0, pop2.objectNames.length, beforeHalf);
// use scalar version if index is in this range.
const vectorRange: [number, number][] = [
[vBegin, vHalf],
[vHalf, vEnd],
];
await Promise.all(vectorRange.map((range, index) => ({
range,
// Assign each range a dedicated SPEC, to avoid race conditions.
spec: rangeSPEC(range, index),
})).map(async ({ range, spec }) => {
// For each range, linking the objects and test it.
const [a, b] = range;
const pop2Bench = mkPop2Bench(spec);
console.log(`Linking objects. Range in [${a}, ${b}) used vector version. others are scalar version.`);
await pop2Bench.link(names =>
names.map((name, index) => {
if (name === "grid.fppized.o" || index == 207) {
console.log(`${name} is a bad object!`);
return scalar(name);
}
// If index is in range [a, b), use the vector version.
return a <= index && index < b ? vector(name) : scalar(name);
}));
const serviceName = `${pop2Bench.benchData().num}-${a}-${b}`;
checkedSpawnSync("systemd-run",
[
...systemdRunOptions({
scope: false,
unit: `spec-${serviceName}`,
user: true,
}),
"runcpu",
...runcpuOptions({
benchmarks: [pop2Bench.benchData().num.toString()],
config: "clang-O2.cfg",
workload: "ref",
buildType: "nobuild",
outputFormat: ["text", "config"]
}),
], { stdio: "inherit", env: spec.getEnvironment() });
}));
};
(async () => {
const buildPathObj = (buildBase: string) => (build: string) => (objname: string) => path.join(buildBase, build, objname);
const buildDirs = ["build_vector", "build_scalar_vanilla"];
const specs = [
"spec2017",
"spec2017-2",
].map(s => path.resolve(SW_AUTOVEC, s)).map(mkSPEC);
// To test which version works, for grid.
await (async (spec: SPEC) => {
const pop2Bench = mkPop2Bench(spec);
const [vector, scalar] = buildDirs.map(dir => buildPathObj(spec.buildpath(pop2))(dir));
await pop2Bench.link(names => {
return names.flatMap(p => {
if (p == "grid.fppized.o") {
const functionList = [
"grid_.o",
"grid_area_masks_.o",
"grid_calc_tpoints_.o",
"grid_cf_area_avg_.o",
"grid_compute_dz_.o",
"grid_fill_points_.o",
"grid_horiz_grid_internal_.o",
"grid_init_grid1_.o",
"grid_init_grid2_.o",
"grid_landmasks_.o",
"grid_read_bottom_cell_.o",
"grid_read_horiz_grid_.o",
"grid_read_topography_.o",
"grid_read_vert_grid_.o",
"grid_remove_isolated_points_.o",
"grid_remove_points_.o",
"grid_smooth_topography_.o",
"grid_tgrid_to_ugrid_.o",
"grid_topography_bathymetry_.o",
"grid_topography_internal_.o",
"grid_ugrid_to_tgrid_.o",
"grid_vert_grid_internal_.o",
];
return [
...functionList.map(fn => {
const getFunc = (name: string) => path.join(spec.buildpath(pop2), name, fn);
return fn == "grid_landmasks_.o" ? getFunc("function_scalar") : getFunc("function_simd");
}),
path.join(spec.buildpath(pop2), "global-obj.ll.o")
];
}
return vector(p);
});
});
await promisifySpawn(spawn("systemd-run",
[
...systemdRunOptions({
scope: true,
user: true,
unit: "pop2-grid-test",
}),
"runcpu",
...runcpuOptions({
benchmarks: [pop2Bench.benchData().num.toString()],
config: "clang-O2",
workload: "test",
buildType: "nobuild",
outputFormat: ["text", "config"]
}),
], { stdio: "inherit", env: spec.getEnvironment() }));
})(specs[0]);
})();

463
src/spec.ts Normal file
View File

@@ -0,0 +1,463 @@
import * as path from 'path';
import * as fs from "fs";
import { promisify } from "util";
import { SW_AUTOVEC } from "./environment";
import { spec } from 'node:test/reporters';
import { ChildProcess, ChildProcessByStdio } from 'child_process';
import { Readable, Writable } from 'stream';
import { Readline } from 'readline/promises';
import { createInterface } from 'readline';
export interface SPECBenchData {
objectNames: string[];
num: number;
name: string;
exe: string;
}
export const pop2: SPECBenchData = {
objectNames: [
"netcdf/attr.o",
"netcdf/dim.o",
"netcdf/error.o",
"netcdf/fort-attio.o",
"netcdf/fort-control.o",
"netcdf/fort-dim.o",
"netcdf/fort-genatt.o",
"netcdf/fort-geninq.o",
"netcdf/fort-genvar.o",
"netcdf/fort-lib.o",
"netcdf/fort-misc.o",
"netcdf/fort-v2compat.o",
"netcdf/fort-var1io.o",
"netcdf/fort-varaio.o",
"netcdf/fort-vario.o",
"netcdf/fort-varmio.o",
"netcdf/fort-varsio.o",
"netcdf/libvers.o",
"netcdf/nc.o",
"netcdf/ncx.o",
"netcdf/posixio.o",
"netcdf/putget.o",
"netcdf/string.o",
"netcdf/v1hpg.o",
"netcdf/v2i.o",
"netcdf/var.o",
"netcdf/netcdf.o",
"netcdf/typeSizes.o",
"fort.fppized.o",
"mpi.o",
"send.o",
"recv.o",
"collective.o",
"req.o",
"list.o",
"handles.o",
"comm.o",
"group.o",
"time.o",
"pack.o",
"m_IndexBin_char.fppized.o",
"m_IndexBin_integer.fppized.o",
"m_IndexBin_logical.fppized.o",
"m_List.fppized.o",
"m_MergeSorts.fppized.o",
"m_Filename.fppized.o",
"m_FcComms.fppized.o",
"m_Permuter.fppized.o",
"m_SortingTools.fppized.o",
"m_String.fppized.o",
"m_StrTemplate.fppized.o",
"m_chars.fppized.o",
"m_die.fppized.o",
"m_dropdead.fppized.o",
"m_FileResolv.fppized.o",
"m_flow.fppized.o",
"m_inpak90.fppized.o",
"m_ioutil.fppized.o",
"m_mall.fppized.o",
"m_mpif.fppized.o",
"m_mpif90.fppized.o",
"m_mpout.fppized.o",
"m_rankMerge.fppized.o",
"m_realkinds.fppized.o",
"m_stdio.fppized.o",
"m_TraceBack.fppized.o",
"m_zeit.fppized.o",
"get_zeits.o",
"m_MCTWorld.fppized.o",
"m_AttrVect.fppized.o",
"m_GlobalMap.fppized.o",
"m_GlobalSegMap.fppized.o",
"m_GlobalSegMapComms.fppized.o",
"m_Accumulator.fppized.o",
"m_SparseMatrix.fppized.o",
"m_Navigator.fppized.o",
"m_AttrVectComms.fppized.o",
"m_AttrVectReduce.fppized.o",
"m_AccumulatorComms.fppized.o",
"m_GeneralGrid.fppized.o",
"m_GeneralGridComms.fppized.o",
"m_SpatialIntegral.fppized.o",
"m_SpatialIntegralV.fppized.o",
"m_MatAttrVectMul.fppized.o",
"m_Merge.fppized.o",
"m_GlobalToLocal.fppized.o",
"m_ExchangeMaps.fppized.o",
"m_ConvertMaps.fppized.o",
"m_SparseMatrixDecomp.fppized.o",
"m_SparseMatrixToMaps.fppized.o",
"m_SparseMatrixComms.fppized.o",
"m_SparseMatrixPlus.fppized.o",
"m_Router.fppized.o",
"m_Rearranger.fppized.o",
"m_Transfer.fppized.o",
"alloc_mod.fppized.o",
"box_rearrange.fppized.o",
"iompi_mod.fppized.o",
"ionf_mod.fppized.o",
"mct_rearrange.fppized.o",
"nf_mod.fppized.o",
"piodarray.fppized.o",
"pio.fppized.o",
"pio_kinds.fppized.o",
"piolib_mod.fppized.o",
"pio_mpi_utils.fppized.o",
"pionfatt_mod.fppized.o",
"pionfget_mod.fppized.o",
"pionfput_mod.fppized.o",
"pionfread_mod.fppized.o",
"pio_nf_utils.fppized.o",
"pionfwrite_mod.fppized.o",
"pio_quicksort.fppized.o",
"pio_spmd_utils.fppized.o",
"pio_support.fppized.o",
"pio_types.fppized.o",
"pio_utils.fppized.o",
"pnetcdfversion.o",
"rearrange.fppized.o",
"topology.o",
"dead_data_mod.fppized.o",
"dead_mct_mod.fppized.o",
"dead_mod.fppized.o",
"ESMF_AlarmClockMod.fppized.o",
"ESMF_AlarmMod.fppized.o",
"ESMF_BaseMod.fppized.o",
"ESMF_BaseTimeMod.fppized.o",
"ESMF_CalendarMod.fppized.o",
"ESMF_ClockMod.fppized.o",
"ESMF_FractionMod.fppized.o",
"ESMF_Mod.fppized.o",
"ESMF_Stubs.fppized.o",
"ESMF_TimeIntervalMod.fppized.o",
"ESMF_TimeMod.fppized.o",
"f_wrappers.o",
"GPTLget_memusage.o",
"GPTLprint_memusage.o",
"GPTLutil.o",
"mct_mod.fppized.o",
"Meat.fppized.o",
"perf_mod.fppized.o",
"perf_utils.fppized.o",
"seq_cdata_mod.fppized.o",
"seq_comm_mct.fppized.o",
"seq_drydep_mod.fppized.o",
"seq_flds_indices.fppized.o",
"seq_flds_mod.fppized.o",
"seq_infodata_mod.fppized.o",
"seq_io_mod.fppized.o",
"seq_timemgr_mod.fppized.o",
"shr_cal_mod.fppized.o",
"shr_const_mod.fppized.o",
"shr_dmodel_mod.fppized.o",
"shr_file_mod.fppized.o",
"shr_flux_mod.fppized.o",
"shr_jlcp.o",
"shr_kind_mod.fppized.o",
"shr_log_mod.fppized.o",
"shr_map_mod.fppized.o",
"shr_mct_mod.fppized.o",
"shr_mem_mod.fppized.o",
"shr_mpi_mod.fppized.o",
"shr_msg_mod.fppized.o",
"shr_ncread_mod.fppized.o",
"shr_orb_mod.fppized.o",
"shr_pcdf_mod.fppized.o",
"shr_scam_mod.fppized.o",
"shr_strdata_mod.fppized.o",
"shr_stream_mod.fppized.o",
"shr_string_mod.fppized.o",
"shr_sys_mod.fppized.o",
"shr_timer_mod.fppized.o",
"shr_tInterp_mod.fppized.o",
"shr_vmath_fwrap.o",
"shr_vmath_mod.fppized.o",
"threadutil.o",
"wrf_error_fatal.fppized.o",
"wrf_message.fppized.o",
"atm_comp_mct.fppized.o",
"datm_comp_mod.fppized.o",
"datm_shr_mod.fppized.o",
"dlnd_comp_mod.fppized.o",
"lnd_comp_mct.fppized.o",
"dice_comp_mod.fppized.o",
"ice_comp_mct.fppized.o",
"POP_BlocksMod.fppized.o",
"POP_BroadcastMod.fppized.o",
"POP_CommMod.fppized.o",
"POP_ConfigMod.fppized.o",
"POP_ConstantsMod.fppized.o",
"POP_DistributionMod.fppized.o",
"POP_DomainSizeMod.fppized.o",
"POP_ErrorMod.fppized.o",
"POP_FieldMod.fppized.o",
"POP_FinalMod.fppized.o",
"POP_GridDimMod.fppized.o",
"POP_GridHorzMod.fppized.o",
"POP_GridVertMod.fppized.o",
"POP_HaloMod.fppized.o",
"POP_IOUnitsMod.fppized.o",
"POP_InitMod.fppized.o",
"POP_KindsMod.fppized.o",
"POP_MCT_vars_mod.fppized.o",
"POP_RedistributeMod.fppized.o",
"POP_ReductionsMod.fppized.o",
"POP_SolversMod.fppized.o",
"advection.fppized.o",
"baroclinic.fppized.o",
"barotropic.fppized.o",
"blocks.fppized.o",
"broadcast.fppized.o",
"budget_diagnostics.fppized.o",
"cfc11_mod.fppized.o",
"cfc_mod.fppized.o",
"check_mod.fppized.o",
"co2calc.fppized.o",
"communicate.fppized.o",
"constants.fppized.o",
"current_meters.fppized.o",
"diag_bsf.fppized.o",
"diagnostics.fppized.o",
"diags_on_lat_aux_grid.fppized.o",
"distribution.fppized.o",
"domain.fppized.o",
"domain_size.fppized.o",
"drifters.fppized.o",
"ecosys_mod.fppized.o",
"ecosys_parms.fppized.o",
"exit_mod.fppized.o",
"forcing.fppized.o",
"forcing_ap.fppized.o",
"forcing_coupled.fppized.o",
"forcing_fields.fppized.o",
"forcing_pt_interior.fppized.o",
"forcing_s_interior.fppized.o",
"forcing_sfwf.fppized.o",
"forcing_shf.fppized.o",
"forcing_tools.fppized.o",
"forcing_ws.fppized.o",
"gather_scatter.fppized.o",
"global_reductions.fppized.o",
"grid.fppized.o",
"history.fppized.o",
"hmix_aniso.fppized.o",
"hmix_del2.fppized.o",
"hmix_del4.fppized.o",
"hmix_gm.fppized.o",
"hmix_gm_submeso_share.fppized.o",
"horizontal_mix.fppized.o",
"hydro_sections.fppized.o",
"iage_mod.fppized.o",
"ice.fppized.o",
"initial.fppized.o",
"io.fppized.o",
"io_binary.fppized.o",
"io_ccsm.fppized.o",
"io_netcdf.fppized.o",
"io_pio.fppized.o",
"io_tools.fppized.o",
"io_types.fppized.o",
"kinds_mod.fppized.o",
"mix_submeso.fppized.o",
"movie.fppized.o",
"ms_balance.fppized.o",
"msg_mod.fppized.o",
"named_field_mod.fppized.o",
"ocn_communicator.fppized.o",
"ocn_comp_mct.fppized.o",
"operators.fppized.o",
"output.fppized.o",
"overflows.fppized.o",
"passive_tracer_tools.fppized.o",
"passive_tracers.fppized.o",
"pressure_grad.fppized.o",
"prognostic.fppized.o",
"qflux_mod.fppized.o",
"registry.fppized.o",
"restart.fppized.o",
"spacecurve_mod.fppized.o",
"state_mod.fppized.o",
"step_mod.fppized.o",
"surface_hgt.fppized.o",
"sw_absorption.fppized.o",
"tavg.fppized.o",
"tidal_mixing.fppized.o",
"time_management.fppized.o",
"timers.fppized.o",
"topostress.fppized.o",
"tracer_types.fppized.o",
"vertical_mix.fppized.o",
"vmix_const.fppized.o",
"vmix_kpp.fppized.o",
"vmix_rich.fppized.o",
"glc_comp_mct.fppized.o",
"ccsm_driver.fppized.o",
"map_atmatm_mct.fppized.o",
"map_atmice_mct.fppized.o",
"map_atmlnd_mct.fppized.o",
"map_atmocn_mct.fppized.o",
"map_glcglc_mct.fppized.o",
"map_iceice_mct.fppized.o",
"map_iceocn_mct.fppized.o",
"map_lndlnd_mct.fppized.o",
"map_ocnocn_mct.fppized.o",
"map_rofocn_mct.fppized.o",
"map_rofrof_mct.fppized.o",
"map_snoglc_mct.fppized.o",
"map_snosno_mct.fppized.o",
"mrg_x2a_mct.fppized.o",
"mrg_x2g_mct.fppized.o",
"mrg_x2i_mct.fppized.o",
"mrg_x2l_mct.fppized.o",
"mrg_x2o_mct.fppized.o",
"mrg_x2s_mct.fppized.o",
"seq_avdata_mod.fppized.o",
"seq_diag_mct.fppized.o",
"seq_domain_mct.fppized.o",
"seq_flux_mct.fppized.o",
"seq_frac_mct.fppized.o",
"seq_hist_mod.fppized.o",
"seq_rearr_mod.fppized.o",
"seq_rest_mod.fppized.o",
],
exe: "speed_pop2_base.mytest-m64",
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<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,
};
}
export const defaultSPEC = mkSPEC(path.join(SW_AUTOVEC, "spec2017"));
const specTemplate = fs.readFileSync("assets/specTemplate.cfg").toString("utf-8");
const writeFile = promisify(fs.writeFile);
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);
}
interface SPECResult {
format: string;
paths: 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 results: SPECResult[] = [];
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());
results.push({
format: type,
paths: paths,
});
}
// To match if the line contains: "runcpu finished
if (line.startsWith("runcpu finished")) {
resolve(results);
}
});
});
}