Skip to content

Commit 60eb9de

Browse files
authored
[jit-times] Use a single long instead of (long, int, int) (#9484)
The operator+ and operator- operators for `Timestamp` type can produce invalid Timestamps. All of Timestamp parts should be either (0 or positive) or (0 or negative), but statements like `result.milliseconds--` don't take this value into consideration. The reporting is also broken: the tool parses `methods.txt` in the assumption that it is produced by a single-threaded app, and it seems that it's not the case in reality. This is why many `self` timings are negative. Instead of treating a timestamp as a "tuple" of `(long seconds, int milliseconds, int nanoseconds)`, treat it as a single `long nanoseconds`. This reduces the amount of range of times that can be parsed, but the new reduced upper limit of 2262-04-11 gives us around 238 years of not needing to care, while massively simplifying the logic and fixing bugs.
1 parent dfa3046 commit 60eb9de

File tree

2 files changed

+32
-85
lines changed

2 files changed

+32
-85
lines changed

tools/jit-times/Program.cs

+9-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Collections.Generic;
55
using System.IO;
66
using System.Linq;
7+
using System.Numerics;
78
using System.Text.RegularExpressions;
89
using static System.Console;
910

@@ -159,7 +160,7 @@ public static int Main (string [] args)
159160
var info = GetMethodInfo (method);
160161

161162
if (info.state != MethodInfo.State.None && Verbose)
162-
Warning ($"duplicit begin of `{info.method}`");
163+
Warning ($"duplicate begin of `{info.method}`");
163164

164165
info.state = MethodInfo.State.Begin;
165166
info.begin = time;
@@ -183,6 +184,12 @@ public static int Main (string [] args)
183184
info.total = info.done - info.begin;
184185

185186
info.CalcSelfTime ();
187+
if (Verbose) {
188+
if (info.self.nanoseconds < 0)
189+
Warning ($"negative self time for method {method}: {info.self}");
190+
if (info.total.nanoseconds < 0)
191+
Warning ($"negative total time for method {method}: {info.total}");
192+
}
186193

187194
jitMethods.Pop ();
188195

@@ -229,7 +236,7 @@ static Timestamp PrintSortedMethods ()
229236
var info = pair.Value;
230237
WriteLine ($"{info.total.Milliseconds (),10:F2} | {info.self.Milliseconds (),10:F2} | {info.method}");
231238

232-
sum += info.self;
239+
sum += info.self.Positive();
233240
}
234241

235242
return sum;

tools/jit-times/Timestamp.cs

+23-83
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,53 @@
11
using System;
2+
using System.Collections.Generic;
23
using System.Text.RegularExpressions;
34

45
namespace jittimes {
5-
public struct Timestamp : IComparable {
6-
public Int64 seconds;
7-
public int milliseconds;
8-
public int nanoseconds;
9-
6+
public record struct Timestamp (long nanoseconds) : IComparable {
107
static readonly Regex regex = new Regex ("^([0-9]+)s:([0-9]+)::([0-9]+)$");
118

129
public static Timestamp Parse (string time)
1310
{
14-
Timestamp ts = new Timestamp ();
15-
1611
var match = regex.Match (time);
17-
if (!match.Success || match.Groups.Count <= 3) {
18-
ts.seconds = 0;
19-
ts.milliseconds = 0;
20-
ts.nanoseconds = 0;
21-
return ts;
22-
}
12+
if (!match.Success || match.Groups.Count <= 3)
13+
return default;
2314

24-
ts.seconds = Convert.ToInt64 (match.Groups [1].Value);
25-
ts.milliseconds = Convert.ToInt32 (match.Groups [2].Value);
26-
ts.nanoseconds = Convert.ToInt32 (match.Groups [3].Value);
27-
28-
return ts;
15+
var s = Convert.ToInt64 (match.Groups [1].Value);
16+
var ms = Convert.ToInt32 (match.Groups [2].Value);
17+
var ns = Convert.ToInt32 (match.Groups [3].Value);
18+
return new Timestamp (1_000_000*s + 1_000_000*ms + ns);
2919
}
3020

3121
static public Timestamp operator - (Timestamp ts1, Timestamp ts2)
32-
{
33-
Timestamp result = new Timestamp ();
34-
35-
if (ts1.nanoseconds >= ts2.nanoseconds)
36-
result.nanoseconds = ts1.nanoseconds - ts2.nanoseconds;
37-
else {
38-
result.nanoseconds = 1000000 + ts1.nanoseconds - ts2.nanoseconds;
39-
result.milliseconds--;
40-
}
41-
42-
if (ts1.milliseconds >= ts2.milliseconds)
43-
result.milliseconds += ts1.milliseconds - ts2.milliseconds;
44-
else {
45-
result.milliseconds += 1000 + ts1.milliseconds - ts2.milliseconds;
46-
result.seconds--;
47-
}
48-
49-
result.seconds += ts1.seconds - ts2.seconds;
50-
51-
return result;
52-
}
22+
=> new Timestamp (checked (ts1.nanoseconds - ts2.nanoseconds));
5323

5424
static public Timestamp operator + (Timestamp ts1, Timestamp ts2)
55-
{
56-
Timestamp result = new Timestamp {
57-
nanoseconds = ts1.nanoseconds + ts2.nanoseconds
58-
};
59-
60-
if (result.nanoseconds > 1000000) {
61-
result.milliseconds += result.nanoseconds / 1000000;
62-
result.nanoseconds %= 1000000;
63-
}
64-
65-
result.milliseconds += ts1.milliseconds + ts2.milliseconds;
66-
67-
if (result.milliseconds > 1000) {
68-
result.seconds += result.milliseconds / 1000;
69-
result.milliseconds %= 1000;
70-
}
71-
72-
return result;
73-
}
25+
=> new Timestamp (checked (ts1.nanoseconds + ts2.nanoseconds));
7426

7527
public override string ToString ()
7628
{
77-
var sec = seconds != 0 ? $"{seconds}(s):" : "";
78-
79-
return $"{sec}{milliseconds}::{nanoseconds}";
29+
var remainder = Math.Abs (nanoseconds);
30+
var s = remainder / 1_000_000_000;
31+
remainder -= 1_000_000_000*s;
32+
var ms = remainder / 1_000_000;
33+
var ns = remainder - 1_000_000*ms;
34+
var sign = nanoseconds < 0 ? "-" : "";
35+
var sec = s != 0 ? $"{s}(s):" : "";
36+
return $"{sign}{sec}{ms}::{ns}";
8037
}
8138

39+
public Timestamp Positive ()
40+
=> new Timestamp (Math.Max (0L, nanoseconds));
41+
8242
public double Milliseconds ()
83-
{
84-
return seconds * 1000.0 + (double)milliseconds + nanoseconds / 1000000.0;
85-
}
43+
=> nanoseconds / 1_000_000;
8644

8745
public int CompareTo (object o)
8846
{
8947
if (!(o is Timestamp other))
9048
throw new ArgumentException ("Object is not a Timestamp");
9149

92-
if (seconds > other.seconds)
93-
return 1;
94-
95-
if (seconds < other.seconds)
96-
return -1;
97-
98-
if (milliseconds > other.milliseconds)
99-
return 1;
100-
101-
if (milliseconds < other.milliseconds)
102-
return -1;
103-
104-
if (nanoseconds > other.nanoseconds)
105-
return 1;
106-
107-
if (nanoseconds < other.nanoseconds)
108-
return -1;
109-
110-
return 0;
50+
return Comparer<long>.Default.Compare (this.nanoseconds, other.nanoseconds);
11151
}
11252
}
11353
}

0 commit comments

Comments
 (0)