mirror of
https://github.com/dlang/dmd.git
synced 2025-04-26 13:10:12 +03:00
248 lines
7.6 KiB
D
Executable file
248 lines
7.6 KiB
D
Executable file
#!/usr/bin/env rdmd
|
|
/**
|
|
* This is a driver script that runs the benchmarks.
|
|
*
|
|
* Copyright: Copyright Martin Nowak 2011 -.
|
|
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
* Authors: Martin Nowak
|
|
*/
|
|
import std.stdio;
|
|
|
|
extern(C) __gshared bool rt_cmdline_enabled = false;
|
|
|
|
struct Config
|
|
{
|
|
string pattern = r".*\.d", dmd = "dmd", dflags = "-mcpu=native -O -release -inline", args;
|
|
bool help, verbose, compile = true;
|
|
uint repeat = 10;
|
|
}
|
|
|
|
string runCmd(string cmd, bool verbose, in char[] workDir = null)
|
|
{
|
|
import std.exception : enforce;
|
|
import std.process : executeShell, Config;
|
|
|
|
if (verbose) writeln(cmd);
|
|
auto res = executeShell(cmd, null, Config.none, size_t.max, workDir);
|
|
enforce(res.status == 0, res.output);
|
|
return res.output;
|
|
}
|
|
|
|
string extraSourceOf(string path)
|
|
{
|
|
import std.path, std.string;
|
|
|
|
string dir = path.dirName;
|
|
while(dir != path)
|
|
{
|
|
string base = dir.baseName;
|
|
if(base.endsWith(".extra"))
|
|
return dir[0..$-6] ~ ".d";
|
|
path = dir;
|
|
dir = path.dirName;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void runTests(Config cfg)
|
|
{
|
|
import std.algorithm, std.file, std.path, std.regex, std.string;
|
|
|
|
if (exists("gcx.log")) remove("gcx.log");
|
|
|
|
string[] sources;
|
|
string[string] extra_sources;
|
|
auto re = regex(cfg.pattern, "g");
|
|
auto cwd = __FILE_FULL_PATH__.dirName;
|
|
auto self = buildPath(cwd, "runbench.d");
|
|
foreach(DirEntry src; dirEntries(cwd, "*.d", SpanMode.depth))
|
|
{
|
|
if (!src.isFile || src.name == self || src.name.withExtension(".ignore").exists)
|
|
continue;
|
|
|
|
string mainsrc = extraSourceOf(src.name);
|
|
if (mainsrc)
|
|
{
|
|
if (cfg.verbose) writeln(src.name, " is extra file for ", mainsrc);
|
|
extra_sources[mainsrc] ~= " " ~ src.name;
|
|
}
|
|
else if (!match(src.name, re).empty)
|
|
sources ~= src.name;
|
|
}
|
|
|
|
import std.parallelism : parallel;
|
|
immutable bindir = absolutePath("bin", cwd);
|
|
immutable objdir = absolutePath("obj", cwd);
|
|
|
|
foreach(ref src; sources.parallel(1))
|
|
{
|
|
version (Windows) enum exe = "exe"; else enum exe = "";
|
|
auto bin = buildPath(bindir, src.relativePath(cwd).setExtension(exe));
|
|
auto obj = buildPath(objdir, src.relativePath(cwd).setExtension(exe));
|
|
auto cmd = std.string.format("%s %s -op -od%s -of%s %s", cfg.dmd, cfg.dflags, obj, bin, src);
|
|
if (auto ex = src in extra_sources)
|
|
cmd ~= " -I" ~ src[0..$-2] ~ ".extra" ~ *ex;
|
|
if (cfg.compile)
|
|
{
|
|
writeln("COMPILING ", src);
|
|
runCmd(cmd, cfg.verbose);
|
|
}
|
|
src = bin;
|
|
}
|
|
|
|
foreach(bin; sources)
|
|
{
|
|
import core.time : Duration;
|
|
import std.algorithm : min;
|
|
import std.datetime.stopwatch : AutoStart, StopWatch;
|
|
auto sw = StopWatch(AutoStart.yes);
|
|
auto minDur = Duration.max;
|
|
string minGCProf;
|
|
|
|
immutable benchName = bin.baseName.stripExtension;
|
|
|
|
void report(string pfx, Duration dur, string gcProf)
|
|
{
|
|
auto parts = dur.split!("seconds", "msecs");
|
|
if (gcProf.length)
|
|
writefln("%s %-16s %s.%03s s, %s", pfx, benchName, parts.seconds, parts.msecs, gcProf);
|
|
else
|
|
writefln("%s %-16s %s.%03s s", pfx, benchName, parts.seconds, parts.msecs);
|
|
}
|
|
|
|
auto cmd = bin ~ " " ~ cfg.args;
|
|
foreach (_; 0 .. cfg.repeat)
|
|
{
|
|
sw.reset;
|
|
auto output = runCmd(cmd, cfg.verbose, cwd);
|
|
auto dur = cast(Duration)sw.peek;
|
|
|
|
auto parts = dur.split!("seconds", "msecs");
|
|
|
|
if (cfg.verbose) stdout.write(output);
|
|
|
|
string gcProf;
|
|
if (exists("gcx.log"))
|
|
{
|
|
auto tgt = bin.setExtension("gcx.log");
|
|
rename("gcx.log", tgt);
|
|
auto lines = File(tgt, "r").byLine()
|
|
.find!(ln => ln.canFind("GC summary:"));
|
|
if (!lines.empty) gcProf = lines.front.find("GC summary:")[11..$].idup;
|
|
}
|
|
else
|
|
{
|
|
auto lines = output.splitter(ctRegex!`\r\n|\r|\n`)
|
|
.find!(ln => ln.startsWith("GC summary:"));
|
|
if (!lines.empty) gcProf = lines.front[11..$];
|
|
}
|
|
if (cfg.verbose) report("RUN", dur, gcProf);
|
|
|
|
if (dur < minDur)
|
|
{
|
|
minDur = dur;
|
|
minGCProf = gcProf;
|
|
}
|
|
}
|
|
report("MIN", minDur, minGCProf);
|
|
}
|
|
}
|
|
|
|
void printHelp()
|
|
{
|
|
import std.ascii : nl=newline;
|
|
auto helpString =
|
|
"usage: runbench [-h|--help] [-v|--verbose] [-r n|--repeat=n] [<test_regex>] [<dflags>] [-- <runargs>]"~nl~nl~
|
|
|
|
" tests - Regular expressions to select tests. Default: '.*\\.d'"~nl~
|
|
" dflags - Flags passed to compiler. Default: '-mcpu=native -O -release -inline'"~nl~
|
|
" runargs - Arguments passed to each test, e.g. '--DRT-gcopt=profile=1'"~nl~nl~
|
|
"Don't pass any argument to run all tests with optimized builds.";
|
|
|
|
writeln(helpString);
|
|
}
|
|
|
|
Config parseArgs(string[] args)
|
|
{
|
|
import std.algorithm, std.string : join;
|
|
|
|
Config cfg;
|
|
{
|
|
import std.range : only;
|
|
string[] tmp = args;
|
|
if (findSkip(tmp, only("--")))
|
|
{
|
|
import std.process : escapeShellCommand;
|
|
cfg.args = escapeShellCommand(tmp);
|
|
args = args[0 .. $ - 1 - tmp.length];
|
|
}
|
|
}
|
|
|
|
import std.getopt;
|
|
getopt(args, config.stopOnFirstNonOption,
|
|
config.passThrough,
|
|
"h|help", &cfg.help,
|
|
"v|verbose", &cfg.verbose,
|
|
"N|no-compile", (string option) { cfg.compile = false; },
|
|
"dflags", &cfg.dflags,
|
|
"r|repeat", &cfg.repeat);
|
|
|
|
if (args.length >= 2 && !args[1].startsWith("-"))
|
|
{
|
|
cfg.pattern = args[1];
|
|
args = args.remove(1);
|
|
}
|
|
|
|
if (args.length > 1)
|
|
cfg.dflags = join(args[1 .. $], " ");
|
|
|
|
return cfg;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
template cfg(N...)
|
|
{
|
|
static Config cfg(T...)(T vals) if (T.length == N.length)
|
|
{
|
|
Config res;
|
|
foreach (i, ref v; vals) __traits(getMember, res, N[i]) = v;
|
|
return res;
|
|
}
|
|
}
|
|
|
|
import std.typecons : t=tuple;
|
|
auto check = [
|
|
t(["bin"], cfg),
|
|
t(["bin", "-h"], cfg!("help")(true)),
|
|
t(["bin", "-v"], cfg!("verbose")(true)),
|
|
t(["bin", "-h", "-v"], cfg!("help", "verbose")(true, true)),
|
|
t(["bin", "gcbench"], cfg!("pattern")("gcbench")),
|
|
t(["bin", "-v", "gcbench"], cfg!("pattern", "verbose")("gcbench", true)),
|
|
t(["bin", "-r", "4", "gcbench"], cfg!("pattern", "repeat")("gcbench", 4)),
|
|
t(["bin", "-g"], cfg!("dflags")("-g")),
|
|
t(["bin", "gcbench", "-g"], cfg!("pattern", "dflags")("gcbench", "-g")),
|
|
t(["bin", "-r", "2", "gcbench", "-g"], cfg!("pattern", "dflags", "repeat")("gcbench", "-g", 2)),
|
|
t(["bin", "--", "--DRT-gcopt=profile:1"], cfg!("args")("--DRT-gcopt=profile:1")),
|
|
t(["bin", "--", "foo", "bar"], cfg!("args")("foo bar")),
|
|
t(["bin", "gcbench", "--", "args"], cfg!("pattern", "args")("gcbench", "args")),
|
|
t(["bin", "--repeat=5", "gcbench", "--", "args"], cfg!("pattern", "args", "repeat")("gcbench", "args", 5)),
|
|
];
|
|
foreach (pair; check)
|
|
assert(parseArgs(pair[0]) == pair[1]);
|
|
}
|
|
|
|
void main()
|
|
{
|
|
import core.runtime;
|
|
// use Runtime.args for --DRT-gcopt
|
|
auto cfg = parseArgs(Runtime.args);
|
|
if (cfg.help) return printHelp();
|
|
|
|
import std.process : env=environment;
|
|
cfg.dmd = env.get("DMD", "dmd");
|
|
if (cfg.compile)
|
|
writeln("compiler: "~cfg.dmd~' '~cfg.dflags);
|
|
|
|
runTests(cfg);
|
|
}
|