You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2024/08/01 10:54:32 UTC
(ignite-3) branch main updated: IGNITE-22872 Add client heartbeat benchmarks (#4165)
This is an automated email from the ASF dual-hosted git repository.
ptupitsyn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new 1e8959c0a0 IGNITE-22872 Add client heartbeat benchmarks (#4165)
1e8959c0a0 is described below
commit 1e8959c0a000f0901085eb0b11b37db4299fa72a
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Thu Aug 1 13:54:27 2024 +0300
IGNITE-22872 Add client heartbeat benchmarks (#4165)
* Java client: add `ClientHeartbeatBenchmark`
* ~17 us / op
* Check profiler - no obvious issues
* Remove unnecessary `channelReadComplete` override
* .NET client: add `HeartbeatBenchmark` (uses real server via `PlatformBenchmarkNodeRunner`)
* ~15 us / op
* Check profiler - no obvious issues
* Remove `HasFlag` usage to avoid enum boxing (minor allocation reduction)
---
.../handler/ClientInboundMessageHandler.java | 12 +-
.../io/netty/NettyClientConnectionMultiplexer.java | 1 +
.../benchmarks/ClientHeartbeatBenchmark.java | 124 +++++++++++++++++++++
.../{ => benchmarks}/ClientPutGetBenchmark.java | 8 +-
.../Apache.Ignite.Benchmarks/HeartbeatBenchmark.cs | 52 +++++++++
.../dotnet/Apache.Ignite.Benchmarks/Program.cs | 3 +-
.../dotnet/Apache.Ignite/Internal/ClientSocket.cs | 6 +-
.../runner/app/PlatformBenchmarkNodeRunner.java | 4 +
8 files changed, 193 insertions(+), 17 deletions(-)
diff --git a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
index 2f3c0ad04f..8d1fcd6e76 100644
--- a/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
+++ b/modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientInboundMessageHandler.java
@@ -306,7 +306,7 @@ public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter im
ByteBuf byteBuf = (ByteBuf) msg;
// Each inbound handler in a pipeline has to release the received messages.
- var unpacker = getUnpacker(byteBuf);
+ var unpacker = new ClientMessageUnpacker(byteBuf);
metrics.bytesReceivedAdd(byteBuf.readableBytes() + ClientMessageCommon.HEADER_SIZE);
// Packer buffer is released by Netty on send, or by inner exception handlers below.
@@ -551,10 +551,6 @@ public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter im
return new ClientMessagePacker(alloc.buffer());
}
- private static ClientMessageUnpacker getUnpacker(ByteBuf buf) {
- return new ClientMessageUnpacker(buf);
- }
-
private void processOperation(ChannelHandlerContext ctx, ClientMessageUnpacker in, ClientMessagePacker out) {
long requestId = -1;
int opCode = -1;
@@ -826,12 +822,6 @@ public class ClientInboundMessageHandler extends ChannelInboundHandlerAdapter im
}
}
- /** {@inheritDoc} */
- @Override
- public void channelReadComplete(ChannelHandlerContext ctx) {
- ctx.flush();
- }
-
/** {@inheritDoc} */
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
diff --git a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
index fa4264b08a..9adf9d1961 100644
--- a/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
+++ b/modules/client/src/main/java/org/apache/ignite/internal/client/io/netty/NettyClientConnectionMultiplexer.java
@@ -78,6 +78,7 @@ public class NettyClientConnectionMultiplexer implements ClientConnectionMultipl
try {
bootstrap.group(workerGroup);
bootstrap.channel(NioSocketChannel.class);
+ bootstrap.option(ChannelOption.TCP_NODELAY, true);
bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) clientCfg.connectTimeout());
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
diff --git a/modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientHeartbeatBenchmark.java b/modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientHeartbeatBenchmark.java
new file mode 100644
index 0000000000..10ab5ddafa
--- /dev/null
+++ b/modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientHeartbeatBenchmark.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.client.benchmarks;
+
+import io.netty.util.ResourceLeakDetector;
+import io.netty.util.ResourceLeakDetector.Level;
+import java.util.concurrent.TimeUnit;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.client.TestServer;
+import org.apache.ignite.client.fakes.FakeIgnite;
+import org.apache.ignite.internal.client.ClientChannel;
+import org.apache.ignite.internal.client.TcpIgniteClient;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.TearDown;
+import org.openjdk.jmh.runner.Runner;
+import org.openjdk.jmh.runner.RunnerException;
+import org.openjdk.jmh.runner.options.Options;
+import org.openjdk.jmh.runner.options.OptionsBuilder;
+import org.openjdk.jmh.runner.options.TimeValue;
+
+/**
+ * Heartbeat (ping) benchmark - measures the simplest client-server interaction.
+ *
+ * <p>Results on i9-12900H, openjdk 11.0.23, Ubuntu 22.04:
+ * Benchmark Mode Cnt Score Error Units
+ * ClientHeartbeatBenchmark.heartbeat sample 1297810 17.333 ± 0.021 us/op
+ * ClientHeartbeatBenchmark.heartbeat:gc.alloc.rate sample 3 27.942 ± 16.104 MB/sec
+ * ClientHeartbeatBenchmark.heartbeat:gc.alloc.rate.norm sample 3 512.781 ± 242.667 B/op
+ * ClientHeartbeatBenchmark.heartbeat:gc.count sample 3 6.000 counts
+ * ClientHeartbeatBenchmark.heartbeat:gc.time sample 3 18.000 ms
+ * ClientHeartbeatBenchmark.heartbeat:jfr sample NaN ---
+ * ClientHeartbeatBenchmark.heartbeat:p0.00 sample 13.120 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.50 sample 16.480 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.90 sample 20.480 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.95 sample 21.408 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.99 sample 25.632 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.999 sample 94.336 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p0.9999 sample 160.880 us/op
+ * ClientHeartbeatBenchmark.heartbeat:p1.00 sample 3690.496 us/op
+ */
+@State(Scope.Benchmark)
+public class ClientHeartbeatBenchmark {
+ private TestServer testServer;
+
+ private TcpIgniteClient client;
+
+ private ClientChannel channel;
+
+ /**
+ * Init.
+ */
+ @Setup
+ public void init() {
+ testServer = new TestServer(1000, new FakeIgnite("server-1"));
+
+ client = (TcpIgniteClient) IgniteClient.builder()
+ .addresses("127.0.0.1:" + testServer.port())
+ .build();
+
+ channel = client.channel().channels().get(0);
+
+ ResourceLeakDetector.setLevel(Level.DISABLED);
+ }
+
+ /**
+ * Tear down.
+ */
+ @TearDown
+ public void tearDown() throws Exception {
+ client.close();
+ testServer.close();
+ }
+
+ /**
+ * Heartbeat benchmark.
+ */
+ @Benchmark
+ public void heartbeat() {
+ channel.heartbeatAsync(null).join();
+ }
+
+ /**
+ * Runner.
+ *
+ * @param args Arguments.
+ * @throws RunnerException Exception.
+ */
+ public static void main(String[] args) throws RunnerException {
+ Options opt = new OptionsBuilder()
+ .include(ClientHeartbeatBenchmark.class.getSimpleName())
+ .mode(Mode.SampleTime)
+ .timeUnit(TimeUnit.MICROSECONDS)
+ .addProfiler("gc")
+ .addProfiler("jfr")
+ .warmupIterations(3)
+ .warmupTime(TimeValue.seconds(3))
+ .measurementIterations(3)
+ .measurementTime(TimeValue.seconds(15))
+ .forks(1)
+ .threads(1)
+ .build();
+
+ new Runner(opt).run();
+ }
+}
diff --git a/modules/client/src/test/java/org/apache/ignite/client/ClientPutGetBenchmark.java b/modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientPutGetBenchmark.java
similarity index 95%
rename from modules/client/src/test/java/org/apache/ignite/client/ClientPutGetBenchmark.java
rename to modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientPutGetBenchmark.java
index 2186566968..64656cf17b 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/ClientPutGetBenchmark.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/benchmarks/ClientPutGetBenchmark.java
@@ -15,9 +15,13 @@
* limitations under the License.
*/
-package org.apache.ignite.client;
+package org.apache.ignite.client.benchmarks;
+import io.netty.util.ResourceLeakDetector;
+import io.netty.util.ResourceLeakDetector.Level;
import org.apache.ignite.Ignite;
+import org.apache.ignite.client.IgniteClient;
+import org.apache.ignite.client.TestServer;
import org.apache.ignite.client.fakes.FakeIgnite;
import org.apache.ignite.client.fakes.FakeIgniteTables;
import org.apache.ignite.table.RecordView;
@@ -119,6 +123,8 @@ public class ClientPutGetBenchmark {
* @throws RunnerException Exception.
*/
public static void main(String[] args) throws RunnerException {
+ ResourceLeakDetector.setLevel(Level.DISABLED);
+
Options opt = new OptionsBuilder()
.include(ClientPutGetBenchmark.class.getSimpleName())
.addProfiler("gc")
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/HeartbeatBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/HeartbeatBenchmark.cs
new file mode 100644
index 0000000000..1f497581d4
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/HeartbeatBenchmark.cs
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+namespace Apache.Ignite.Benchmarks;
+
+using System.Linq;
+using System.Threading.Tasks;
+using BenchmarkDotNet.Attributes;
+using Internal;
+
+/// <summary>
+/// Results on i9-12900H, .NET SDK 6.0.3224, Ubuntu 22.04:
+/// | Method | Mean | Error | StdDev |
+/// |---------- |---------:|---------:|---------:|
+/// | Heartbeat | 15.62 us | 0.386 us | 1.121 us |.
+/// </summary>
+[SimpleJob]
+public class HeartbeatBenchmark
+{
+ private IgniteClientInternal? _client;
+ private ClientSocket? _socket;
+
+ [GlobalSetup]
+ public async Task GlobalSetup()
+ {
+ // Requires PlatformBenchmarkNodeRunner class to be started.
+ var cfg = new IgniteClientConfiguration("127.0.0.1:10420");
+
+ _client = (IgniteClientInternal)await IgniteClient.StartAsync(cfg);
+ _socket = _client.Socket.GetSockets().Single();
+ }
+
+ [GlobalCleanup]
+ public void GlobalCleanup() => _client?.Dispose();
+
+ [Benchmark]
+ public async Task Heartbeat() => await _socket!.HeartbeatAsync();
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
index 2bb6cd7c9d..51c99d06b3 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Program.cs
@@ -18,10 +18,9 @@
namespace Apache.Ignite.Benchmarks;
using BenchmarkDotNet.Running;
-using Table;
internal static class Program
{
// IMPORTANT: Disable Netty leak detector when using a real Ignite server for benchmarks.
- private static void Main() => BenchmarkRunner.Run<TupleGetBenchmarks>();
+ private static void Main() => BenchmarkRunner.Run<HeartbeatBenchmark>();
}
diff --git a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
index dec8eb3b0c..b0cdc0ffbc 100644
--- a/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
+++ b/modules/platforms/dotnet/Apache.Ignite/Internal/ClientSocket.cs
@@ -813,10 +813,10 @@ namespace Apache.Ignite.Internal
HandlePartitionAssignmentChange(flags, ref reader);
HandleObservableTimestamp(ref reader);
- var exception = flags.HasFlag(ResponseFlags.Error) ? ReadError(ref reader) : null;
+ var exception = (flags & ResponseFlags.Error) != 0 ? ReadError(ref reader) : null;
response.Position += reader.Consumed;
- if (flags.HasFlag(ResponseFlags.Notification))
+ if ((flags & ResponseFlags.Notification) != 0)
{
return HandleNotification(requestId, exception, response);
}
@@ -882,7 +882,7 @@ namespace Apache.Ignite.Internal
private void HandlePartitionAssignmentChange(ResponseFlags flags, ref MsgPackReader reader)
{
- if (flags.HasFlag(ResponseFlags.PartitionAssignmentChanged))
+ if ((flags & ResponseFlags.PartitionAssignmentChanged) != 0)
{
long timestamp = reader.ReadInt64();
diff --git a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
index 8df2e973dd..b0948be8e6 100644
--- a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
+++ b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/runner/app/PlatformBenchmarkNodeRunner.java
@@ -17,6 +17,8 @@
package org.apache.ignite.internal.runner.app;
+import io.netty.util.ResourceLeakDetector;
+import io.netty.util.ResourceLeakDetector.Level;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
@@ -56,6 +58,8 @@ public class PlatformBenchmarkNodeRunner {
public static void main(String[] args) throws Exception {
System.out.println("Starting benchmark node runner...");
+ ResourceLeakDetector.setLevel(Level.DISABLED);
+
List<IgniteServer> startedNodes = PlatformTestNodeRunner.startNodes(BASE_PATH, nodesBootstrapCfg);
Ignite ignite = startedNodes.get(0).api();