Skip to content

Commit 191af58

Browse files
committed
%time magic #104
attempt to measure telemetry at a lower level, excluding JShell compile time
1 parent 5ba3d14 commit 191af58

File tree

14 files changed

+218
-205
lines changed

14 files changed

+218
-205
lines changed

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/instrumentation/EvalTimer.java

Lines changed: 0 additions & 34 deletions
This file was deleted.

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/instrumentation/WallAndCpuTimer.java

Lines changed: 0 additions & 78 deletions
This file was deleted.

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/instrumentation/WallTimer.java

Lines changed: 0 additions & 38 deletions
This file was deleted.

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/BaseKernel.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import org.dflib.jjava.jupyter.Extension;
44
import org.dflib.jjava.jupyter.channels.JupyterConnection;
55
import org.dflib.jjava.jupyter.channels.ShellReplyEnvironment;
6-
import org.dflib.jjava.jupyter.instrumentation.EvalTimer;
76
import org.dflib.jjava.jupyter.kernel.comm.CommManager;
87
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
98
import org.dflib.jjava.jupyter.kernel.display.Renderer;
@@ -218,14 +217,13 @@ public Object evalRaw(String source) {
218217
* Creates and returns a builder for an evaluation pipeline.
219218
*/
220219
public <T> EvalBuilder<T> evalBuilder(String source) {
221-
return new SimpleEvalBuilder<>(this, source, EvalTimer.DO_NOTHING);
220+
return new SimpleEvalBuilder<>(this, source);
222221
}
223222

224223
/**
225-
* Evaluates the source code in a way appropriate for a given kernel subclass, and using the provided timer to
226-
* measure evaluation time.
224+
* Evaluates the source code in a way appropriate for a given kernel subclass.
227225
*/
228-
protected abstract Object doEval(String source, EvalTimer timer);
226+
protected abstract Object doEval(String source);
229227

230228
/**
231229
* Inspect the code to get things such as documentation for a function. This is
Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.dflib.jjava.jupyter.kernel;
22

3-
import org.dflib.jjava.jupyter.instrumentation.EvalTimer;
43
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
54

65
public interface EvalBuilder<T> {
@@ -9,10 +8,5 @@ public interface EvalBuilder<T> {
98

109
EvalBuilder<DisplayData> renderResults();
1110

12-
/**
13-
* Installs an evaluation timer for this builder eval call.
14-
*/
15-
EvalBuilder<T> timed(EvalTimer timer);
16-
1711
T eval();
1812
}

jjava-jupyter/src/main/java/org/dflib/jjava/jupyter/kernel/RenderedEvalBuilder.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package org.dflib.jjava.jupyter.kernel;
22

3-
import org.dflib.jjava.jupyter.instrumentation.EvalTimer;
43
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
54

65
public class RenderedEvalBuilder implements EvalBuilder<DisplayData> {
@@ -23,11 +22,6 @@ public RenderedEvalBuilder resolveMagics() {
2322
return new RenderedEvalBuilder(kernel, delegate.resolveMagics());
2423
}
2524

26-
@Override
27-
public EvalBuilder<DisplayData> timed(EvalTimer timer) {
28-
return new RenderedEvalBuilder(kernel, delegate.timed(timer));
29-
}
30-
3125
@Override
3226
public DisplayData eval() {
3327
Object o = delegate.eval();
Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
11
package org.dflib.jjava.jupyter.kernel;
22

3-
import org.dflib.jjava.jupyter.instrumentation.EvalTimer;
43
import org.dflib.jjava.jupyter.kernel.display.DisplayData;
54

65
public class SimpleEvalBuilder<T> implements EvalBuilder<T> {
76

87
private final BaseKernel kernel;
98
private final String source;
10-
private final EvalTimer timer;
119

12-
protected SimpleEvalBuilder(BaseKernel kernel, String source, EvalTimer timer) {
10+
protected SimpleEvalBuilder(BaseKernel kernel, String source) {
1311
this.kernel = kernel;
1412
this.source = source;
15-
this.timer = timer;
16-
}
17-
18-
@Override
19-
public EvalBuilder<T> timed(EvalTimer timer) {
20-
return new SimpleEvalBuilder<>(kernel, source, timer);
2113
}
2214

2315
@Override
2416
public EvalBuilder<T> resolveMagics() {
25-
return new SimpleEvalBuilder<>(kernel, kernel.getMagicsResolver().resolve(source), timer);
17+
return new SimpleEvalBuilder<>(kernel, kernel.getMagicsResolver().resolve(source));
2618
}
2719

2820
@Override
@@ -32,11 +24,6 @@ public EvalBuilder<DisplayData> renderResults() {
3224

3325
@Override
3426
public T eval() {
35-
timer.start();
36-
try {
37-
return (T) kernel.doEval(source, timer);
38-
} finally {
39-
timer.stop();
40-
}
27+
return (T) kernel.doEval(source);
4128
}
4229
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package org.dflib.jjava.jupyter.telemetry;
2+
3+
/**
4+
* Aggregates some telemetry number for a sequence of "measurements". Instead of a single method wrapping a measure code
5+
* invocation lambda, each measurement is done via two separate methods: {@link #measurementStart()} and
6+
* {@link #measurementEnd(Object)}. This is to make the collector minimally invasive and not adding to the call stack
7+
* trace.
8+
*/
9+
public interface TelemetryCollector<I> {
10+
11+
I measurementStart();
12+
13+
void measurementEnd(I startState);
14+
15+
void stop();
16+
17+
TelemetryCollector<Object> DO_NOTHING = new TelemetryCollector<>() {
18+
@Override
19+
public void measurementEnd(Object startState) {
20+
}
21+
22+
@Override
23+
public Object measurementStart() {
24+
return null;
25+
}
26+
27+
@Override
28+
public void stop() {
29+
}
30+
};
31+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.dflib.jjava.jupyter.telemetry;
2+
3+
import java.lang.management.ThreadMXBean;
4+
import java.util.concurrent.atomic.AtomicLong;
5+
6+
public abstract class WallAndCpuTimeCollector implements TelemetryCollector<WallAndCpuTimeCollector.Measure> {
7+
8+
private final ThreadMXBean threadMXBean;
9+
private final AtomicLong totalWT;
10+
private final AtomicLong totalUT;
11+
private final AtomicLong totalCT;
12+
13+
public static boolean canMeasureCpuTimes(ThreadMXBean threadMXBean) {
14+
// according to JavaDocs, isThreadCpuTimeEnabled() can throw in some cases
15+
try {
16+
return threadMXBean.isCurrentThreadCpuTimeSupported() && threadMXBean.isThreadCpuTimeEnabled();
17+
} catch (Exception e) {
18+
return false;
19+
}
20+
}
21+
22+
public WallAndCpuTimeCollector(ThreadMXBean threadMXBean) {
23+
this.threadMXBean = threadMXBean;
24+
this.totalWT = new AtomicLong();
25+
this.totalUT = new AtomicLong();
26+
this.totalCT = new AtomicLong();
27+
}
28+
29+
@Override
30+
public Measure measurementStart() {
31+
return new Measure(
32+
System.nanoTime(),
33+
threadMXBean.getCurrentThreadUserTime(),
34+
threadMXBean.getCurrentThreadCpuTime()
35+
);
36+
}
37+
38+
@Override
39+
public void measurementEnd(Measure m) {
40+
41+
long wt = System.nanoTime();
42+
long ut = threadMXBean.getCurrentThreadUserTime();
43+
long ct = threadMXBean.getCurrentThreadCpuTime();
44+
45+
totalWT.addAndGet(wt - m.wt);
46+
47+
// sanity check
48+
if (m.ut != -1 && ut != -1 && m.ct != -1 && ct != -1) {
49+
totalUT.addAndGet(ut - m.ut);
50+
totalCT.addAndGet(ct - m.ct);
51+
} else {
52+
totalUT.set(-1);
53+
totalCT.set(-1);
54+
}
55+
}
56+
57+
@Override
58+
public void stop() {
59+
onStop(totalWT.get(), totalUT.get(), totalCT.get());
60+
}
61+
62+
protected abstract void onStop(long wallTimeNanos, long userTimeNanos, long totalTimeNanos);
63+
64+
public static class Measure {
65+
final long wt;
66+
final long ut;
67+
final long ct;
68+
69+
public Measure(long wt, long ut, long ct) {
70+
this.ct = ct;
71+
this.wt = wt;
72+
this.ut = ut;
73+
}
74+
}
75+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package org.dflib.jjava.jupyter.telemetry;
2+
3+
import java.util.concurrent.atomic.AtomicLong;
4+
5+
public abstract class WallTimeCollector implements TelemetryCollector<Long> {
6+
7+
private final AtomicLong totalWallTime;
8+
9+
public WallTimeCollector() {
10+
this.totalWallTime = new AtomicLong();
11+
}
12+
13+
@Override
14+
public Long measurementStart() {
15+
return System.nanoTime();
16+
}
17+
18+
@Override
19+
public void measurementEnd(Long startState) {
20+
long delta = System.nanoTime() - startState;
21+
totalWallTime.addAndGet(delta);
22+
}
23+
24+
@Override
25+
public void stop() {
26+
onStop(totalWallTime.get());
27+
}
28+
29+
protected abstract void onStop(long wallTimeNanos);
30+
}

0 commit comments

Comments
 (0)