Skip to content

Instantly share code, notes, and snippets.

@jeffque
Created December 6, 2024 20:41
Show Gist options
  • Save jeffque/c635b172e072f0e56ae9df89c52a3587 to your computer and use it in GitHub Desktop.
Save jeffque/c635b172e072f0e56ae9df89c52a3587 to your computer and use it in GitHub Desktop.
A simple app to show that visualvm cannot sample CPU usage of virtual threads
package jeffque;
import java.util.Scanner;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Supplier;
public class Main {
private static class AckPeter {
static class LongInd {
long v = 0;
long inc() {
return ++v;
}
}
static long compute(AckPeter.Continuation c) {
while (!c.finished()) {
c = c.step();
}
return c.value();
}
static long ackPeter(long m, long n) {
return compute(AckPeter.Continuation.goon(() -> ackermannPeter(m, AckPeter.Continuation.found(n), new AckPeter.LongInd())));
}
interface Continuation {
boolean finished();
long value();
AckPeter.Continuation step();
// computation has over
static AckPeter.Continuation found(long v) {
return new AckPeter.Continuation() {
@Override
public boolean finished() {
return true;
}
@Override
public long value() {
return v;
}
@Override
public AckPeter.Continuation step() {
return this;
}
};
}
// go on computing
static AckPeter.Continuation goon(Supplier<AckPeter.Continuation> nextStep) {
return new AckPeter.Continuation() {
@Override
public boolean finished() {
return false;
}
@Override
public long value() {
return 0;
}
@Override
public AckPeter.Continuation step() {
return nextStep.get();
}
};
}
}
private static AckPeter.Continuation ackermannPeter(long m, AckPeter.Continuation c, AckPeter.LongInd count) {
long p;
// if ((p = count.inc()) % 100_000_000 == 0) {
// System.out.printf("%s:%d passo %d\n", Thread.currentThread().getName(), Thread.currentThread().threadId(), p);
// }
if (!c.finished()) {
return AckPeter.Continuation.goon(() -> {
final var next = c.step();
return AckPeter.Continuation.goon(() -> ackermannPeter(m, next, count));
});
}
long n = c.value();
if (m <= 0) {
return AckPeter.Continuation.found(n + 1);
}
if (n <= 0) {
return AckPeter.Continuation.goon(() -> ackermannPeter(m - 1, AckPeter.Continuation.found(1), count));
}
return AckPeter.Continuation.goon(() ->
ackermannPeter(m - 1,
AckPeter.Continuation.goon(() -> ackermannPeter(m, AckPeter.Continuation.found(n - 1), count
)), count)
);
}
}
private final boolean useVThreads;
private final ExecutorService executorService;
public Main(String... args) {
boolean vthreads = false;
for (int i = 0; i < args.length; i++) {
final var flag = args[i];
int skip = 0;
boolean success = switch (flag) {
case "--help" -> {
showHelpAndQuit();
yield true;
}
case "--vthread" -> {
vthreads = true;
yield true;
}
default -> false;
};
if (!success) {
throw new RuntimeException("Oops, invalid option [" + flag + "]");
}
i += skip;
}
useVThreads = vthreads;
if (useVThreads) {
executorService = Executors.newSingleThreadExecutor(Thread.ofVirtual().factory());
} else {
executorService = Executors.newSingleThreadExecutor();
}
}
private void showHelpAndQuit() {
System.out.println("""
Usage:
--vthread : enable vthread (default: false)
""");
System.exit(0);
}
public static void main(String... args) {
new Main(args).cli();
}
void cli() {
Scanner scanner = new Scanner(System.in);
showInnerHelp();
mainloop: while (true) {
System.out.println("> ");
final var cmd = scanner.nextLine().split(" ");
switch (cmd[0]) {
case "ack" -> {
long m = 4l;
long n = 2l;
switch (cmd.length) {
case 3:
n = Long.parseLong(cmd[2]);
case 2:
m = Long.parseLong(cmd[1]);
}
final var effectiveM = m;
final var effectiveN = n;
register(() -> AckPeter.ackPeter(effectiveM, effectiveN));
}
case "count" -> {
System.out.println("total that was submitted: " + submitted);
System.out.println("total that has completed: " + completed);
}
case "help" -> showInnerHelp();
case "quit" -> {
break mainloop;
}
default -> {
System.out.println("Invalid command!!");
showInnerHelp();
}
}
}
}
private int submitted = 0;
private int completed = 0;
void showInnerHelp() {
System.out.printf("""
Enter a command in the CLI (using vthreads? %b)
ack [m] [n] : run the ackermann peter function
count : show the total of submissions/completions
help : show this help
quit : quit\n
""", useVThreads);
}
void register(Runnable r) {
submitted++;
CompletableFuture.runAsync(r, executorService).whenComplete((res, thr) -> completed++);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment