Why is std.regex slow, well here is one reason!
Richard (Rikki) Andrew Cattermole
richard at cattermole.co.nz
Sat Feb 25 13:55:00 UTC 2023
So there is a problem with time trace handling, it doesn't escape
Windows paths so you end up with an exception on \T tather than \\T.
I've gone ahead and modified the tool, did some cleaning up, added a
second output file that allows for consumption in a spreadsheet
application, sorted by duration automatically.
I'd love to see the time trace switch upstreamed into dmd. We can then
distribute this tool for an out of the box visualization experience that
doesn't require a web browser. And of course globals need work, not just
Windows path escaping ;)
It is an absolutely lovely tool that will ease a lot of peoples concerns
over debugging compile times. Gonna be worth a blog article!
I'll put my version here also:
// Run using: rdmd timeTraceTree.d <your .time-trace file>
// Outputs timetrace.txt in current dir
module timeTraceTree;
import std.stdio;
import std.file;
import std.json;
import std.range;
import std.conv;
import std.algorithm;
File outputTextFile, outputTSVFile;
static string duration_format_string = "%13.3f ";
JSONValue sourceFile;
JSONValue[] metadata; // "M"
JSONValue[] counterdata; // "C"
JSONValue[] processes; // "X"
ulong lineNumberCounter = 1;
int main(string[] args)
{
if (args.length < 1)
return 1;
auto input_json = read(args[1]).to!string;
outputTextFile = File("timetrace.txt", "w");
outputTSVFile = File("timetrace.tsv", "w");
{
sourceFile = parseJSON(input_json);
readMetaData;
constructTree;
constructList;
}
{
outputTextFile.writeln("Timetrace: ", args[1]);
lineNumberCounter++;
outputTextFile.writeln("Metadata:");
lineNumberCounter++;
foreach (node; metadata)
{
outputTextFile.write(" ");
outputTextFile.writeln(node);
lineNumberCounter++;
}
outputTextFile.writeln("Duration (ms)");
lineNumberCounter++;
}
foreach (i, ref child; Node.root.children)
child.print(0, false);
outputTSVFile.writeln("Duration\tText Line
Number\tName\tLocation\tDetail");
foreach (node; Node.all)
outputTSVFile.writeln(node.duration, "\t", node.lineNumber, "\t",
node.name, "\t", node.location, "\t", node.detail);
return 0;
}
void readMetaData()
{
auto beginningOfTime = sourceFile["beginningOfTime"].get!ulong;
auto traceEvents = sourceFile["traceEvents"].get!(JSONValue[]);
// Read meta data
foreach (value; traceEvents)
{
switch (value["ph"].get!string)
{
case "M":
metadata ~= value;
break;
case "C":
counterdata ~= value;
break;
case "X":
processes ~= value;
break;
default: //drop
}
}
// process node = {"ph":"X","name": "Sema1: Module
object","ts":26825,"dur":1477,"loc":"<no file>","args":{"detail":
"","loc":"<no file>"},"pid":101,"tid":101},
// Sort time processes
multiSort!(q{a["ts"].get!ulong < b["ts"].get!ulong},
q{a["dur"].get!ulong > b["dur"].get!ulong})(
processes);
}
void constructTree()
{
// Build tree (to get nicer looking structure lines)
Node*[] parent_stack = [&Node.root]; // each stack item represents
the first uncompleted note of that level in the tree
foreach (ref process; processes)
{
auto last_ts = process["ts"].get!ulong + process["dur"].get!ulong;
size_t parent_idx = 0; // index in parent_stack to which this
item should be added.
foreach (i; 0 .. parent_stack.length)
{
if (last_ts > parent_stack[i].last_ts)
{
// The current process outlasts stack item i. Stop
traversing, parent is i-1;
parent_idx = i - 1;
parent_stack.length = i;
break;
}
parent_idx = i;
}
parent_stack[parent_idx].children ~= Node(&process, last_ts);
parent_stack ~= &parent_stack[parent_idx].children[$ - 1];
Node.count++;
}
}
void constructList()
{
size_t offset;
Node.all.length = Node.count - 1;
void handle(Node* root)
{
Node.all[offset++] = root;
foreach (ref child; root.children)
handle(&child);
}
foreach (ref child; Node.root.children)
handle(&child);
Node.all.sort!((a, b) => a.duration > b.duration);
}
struct Node
{
Node[] children;
JSONValue* json;
ulong last_ts; // represents the last timestamp of this node (i.e.
ts + dur)
ulong lineNumber;
string name;
ulong duration;
string location;
string detail;
this(JSONValue* json, ulong last_ts)
{
this.json = json;
this.last_ts = last_ts;
if ((*json).type == JSONType.object && "dur" in (*json))
{
this.duration = (*json)["dur"].get!ulong;
this.name = (*json)["name"].get!string;
this.location = (*json)["args"]["loc"].get!string;
this.detail = (*json)["args"]["detail"].get!string;
}
}
void print(uint indentLevel, bool last_child)
{
char[] identPrefix = getIdentPrefix(indentLevel, last_child);
import std.stdio;
if (last_child)
{
identPrefix[$ - 4] = ' ';
identPrefix[$ - 3 .. $] = "\u2514";
}
else
identPrefix[$ - 2 .. $] = " |";
outputTextFile.writef(duration_format_string,
cast(double)(*this.json)["dur"].get!ulong / 1000);
outputTextFile.write(identPrefix);
outputTextFile.write("- ", this.name);
outputTextFile.write(", ", this.detail);
outputTextFile.writeln(", ", this.location);
this.lineNumber = lineNumberCounter;
lineNumberCounter++;
if (last_child)
identPrefix[$ - 4 .. $] = ' ';
foreach (i, ref child; this.children)
child.print(indentLevel + 1, i == this.children.length - 1);
}
static Node root = Node(new JSONValue("Tree root"), ulong.max);
static Node*[] all;
static size_t count = 1;
}
char[] getIdentPrefix(uint indentLevel, bool last_child)
{
static char[] buffer;
size_t needed = ((indentLevel + 1) * 2) + (last_child * 2);
if (buffer.length < needed)
buffer.length = needed;
return buffer;
}
More information about the Digitalmars-d
mailing list