Conversation
…anualIoEventLoopGroup
…Y_SCHEDULER), add mockless mode, and simplify benchmark runner configuration
c434b52 to
5017502
Compare
…ss+pidstat crash, add BACKLOG - README: replace toy examples with practical ones showing lscpu topology analysis, perf stat interpretation, and NUMA-aware cpuset allocation - Fix MOCK_PID unbound variable when running mockless mode with pidstat enabled - Add BACKLOG.md with investigation findings on kernel CPU accounting gap
|
@copilot scrutinize If the mockless behavior is correct and same while running with a netty scheduler vs virtual netty modes |
|
@franz1981 I've opened a new pull request, #99, to work on those changes. Once the pull request is ready, I'll request review from you. |
|
@copilot nope just work on this PR and suggest changes in case you think there is an issue instead |
|
@franz1981 I've opened a new pull request, #100, to work on those changes. Once the pull request is ready, I'll request review from you. |
There was a problem hiding this comment.
Pull request overview
This PR simplifies the benchmark runner configuration by introducing a consolidated “mode” concept, updating the runner script/README to prefer CLI flags, and adding a new Netty “virtual IO loop” implementation used by the benchmarks.
Changes:
- Add
VirtualMultithreadManualIoEventLoopGroup+ManualIoEventLoopTaskto run Netty IO event loops on virtual threads. - Simplify benchmark runner modes (
NON_VIRTUAL_NETTY,REACTIVE,VIRTUAL_NETTY,NETTY_SCHEDULER) and add a--mocklessfast-path. - Refactor
run-benchmark.sh+ README toward CLI-first configuration and updated examples.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| core/src/main/java/io/netty/loom/VirtualMultithreadManualIoEventLoopGroup.java | New event loop group that spawns a virtual thread per manual IO event loop task. |
| core/src/main/java/io/netty/loom/ManualIoEventLoopTask.java | New runnable ManualIoEventLoop wrapper that drives IO/task execution in a loop. |
| benchmark-runner/src/test/java/io/netty/loom/benchmark/runner/BenchmarkIntegrationTest.java | Narrows/adjusts integration test matrix to the new Mode-based server configuration. |
| benchmark-runner/src/main/java/io/netty/loom/benchmark/runner/ReactiveHandoffHandler.java | Simplifies reactive handler configuration and removes timeout toggles. |
| benchmark-runner/src/main/java/io/netty/loom/benchmark/runner/HandoffHttpServer.java | Introduces Mode + mockless support and rewires server setup accordingly. |
| benchmark-runner/scripts/run-benchmark.sh | CLI-flag parsing, cpuset variable renames, mode-based JVM args, and mockless support. |
| benchmark-runner/README.md | Documents CLI-first usage, new modes, mockless mode, and updated benchmarking guidance. |
| BACKLOG.md | Adds a research/ideas backlog document for benchmarking/accounting observations. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (uri.equals("/") || uri.startsWith("/fruits")) { | ||
| // Hand off to virtual thread for blocking processing | ||
| orderedExecutorService.execute(() -> { | ||
| doBlockingProcessing(ctx, eventLoop, keepAlive); | ||
| }); | ||
| // Hand off to virtual thread for processing | ||
| if (mockless) { | ||
| orderedExecutorService.execute(() -> { | ||
| doMocklessProcessing(ctx, eventLoop, keepAlive); | ||
| }); | ||
| } else { |
There was a problem hiding this comment.
mockless introduces a new execution path (skipping the HTTP call and using CACHED_JSON_BYTES) but there’s no integration test exercising --mockless (including error handling and keep-alive behavior). Consider adding a parameterized test configuration for mockless=true to ensure this mode keeps working.
| private static final long RUNNING_YIELD_US = TimeUnit.MICROSECONDS | ||
| .toNanos(Integer.getInteger("io.netty.loom.running.yield.us", 1)); |
There was a problem hiding this comment.
RUNNING_YIELD_US is computed in nanoseconds (TimeUnit.MICROSECONDS.toNanos(...)), so the name/unit is misleading and makes the subsequent run(..., RUNNING_YIELD_US) calls hard to reason about. Rename the constant (or store the raw microseconds and convert at call sites) so the unit matches the identifier.
| --threads <n> Number of event loop threads (default: 1) | ||
| --io <epoll|nio|io_uring> I/O type (default: epoll) | ||
| --mockless Skip mock server; do Jackson work inline (default: off) | ||
| --mode <mode> Server mode (default: virtual_thread) |
There was a problem hiding this comment.
The help text says --mode <mode> defaults to virtual_thread, but main() initializes mode = Mode.NON_VIRTUAL_NETTY. This is user-visible and will cause confusion when running the benchmark runner. Update the printed default (and any mode naming) to match the actual default behavior.
| --mode <mode> Server mode (default: virtual_thread) | |
| --mode <mode> Server mode (default: NON_VIRTUAL_NETTY) |
benchmark-runner/src/main/java/io/netty/loom/benchmark/runner/ReactiveHandoffHandler.java
Show resolved
Hide resolved
| SERVER_FJ_PARALLELISM="${SERVER_FJ_PARALLELISM:-}" # ForkJoinPool parallelism (empty = JVM default) | ||
| SERVER_NO_TIMEOUT="${SERVER_NO_TIMEOUT:-false}" # Disable HTTP client timeout | ||
| SERVER_REACTIVE="${SERVER_REACTIVE:-false}" # Use reactive handler with Project Reactor | ||
| SERVER_MODE="${SERVER_MODE:-NON_VIRTUAL_NETTY}" # Server mode: NON_VIRTUAL_NETTY, REACTIVE, VIRTUAL_NETTY |
There was a problem hiding this comment.
The comment describing supported SERVER_MODE values omits NETTY_SCHEDULER, but the script explicitly supports it (and the help text later lists it). Update the comment to reflect the actual supported modes to avoid configuration mistakes.
| SERVER_MODE="${SERVER_MODE:-NON_VIRTUAL_NETTY}" # Server mode: NON_VIRTUAL_NETTY, REACTIVE, VIRTUAL_NETTY | |
| SERVER_MODE="${SERVER_MODE:-NON_VIRTUAL_NETTY}" # Server mode: NON_VIRTUAL_NETTY, REACTIVE, VIRTUAL_NETTY, NETTY_SCHEDULER |
| ByteBuf content = Unpooled.copiedBuffer("{\"error\":\"" + e.getMessage() + "\"}", | ||
| CharsetUtil.UTF_8); | ||
| FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, | ||
| HttpResponseStatus.INTERNAL_SERVER_ERROR, content); | ||
| response.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_JSON); | ||
| response.headers().setInt(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes()); | ||
| ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); | ||
| }); |
There was a problem hiding this comment.
In doMocklessProcessing the error path builds a JSON string via concatenation using e.getMessage(). If the message contains quotes, backslashes, or newlines, the response becomes invalid JSON and may leak internal details. Prefer serializing a structured error payload via Jackson (or at least escaping the message) and consider honoring keepAlive/not always closing the connection on errors.
No description provided.