kevin-wu24 commented on code in PR #22669:
URL: https://github.com/apache/kafka/pull/22669#discussion_r3581987670


##########
jmh-benchmarks/src/main/java/org/apache/kafka/jmh/raft/KRaftBenchmarkingCounters.java:
##########
@@ -0,0 +1,157 @@
+/*
+ * 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.kafka.jmh.raft;
+
+import org.apache.kafka.common.protocol.ApiKeys;
+import org.apache.kafka.raft.RaftClientBenchmarkContext;
+
+import org.openjdk.jmh.annotations.AuxCounters;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.infra.BenchmarkParams;
+
+import java.util.Optional;
+
+/**
+ * Secondary, machine-independent work counters reported by the raft 
benchmarks alongside the timing
+ * score, as {@code benchmark:counter} rows.
+ *
+ * <p>Throughout this class, an <em>operation</em> is JMH's unit of work: a 
single invocation of a
+ * {@code @Benchmark}-annotated method. (One operation equals one invocation 
here because we don't use
+ * {@code @OperationsPerInvocation}.) JMH reports the timing score in {@code 
ns/op}, and these work
+ * counters are reported {@code PerOp} to match.
+ *
+ * <p>The per-operation values are integer-exact and should be stable across a 
correct refactor of
+ * {@code KafkaRaftClient}: a flush count moving from 1 to 2 per operation is 
a behavioral diff, not
+ * measurement noise. The counters that are zero on a path (e.g. log flushes 
on a caught-up fetch)
+ * are the most useful tripwires, since zero is speed-independent.
+ */
+@State(Scope.Thread)
+@AuxCounters(AuxCounters.Type.EVENTS)
+public class KRaftBenchmarkingCounters {
+    // Private accumulators: not reported directly (we report the per-op 
values below). Being private,
+    // JMH does not touch them between iterations, so reset() must zero them.
+    private long logFlushesTotal;
+    private long logReadsTotal;
+    private long logTruncationsTotal;
+    private long rpcRequestsSentTotal;
+    private long rpcResponsesSentTotal;
+    private long quorumStateWritesTotal;
+    private long quorumStateReadsTotal;
+
+    // Reported: the number of operations (i.e. @Benchmark method invocations) 
measured in the
+    // iteration, and the divisor for the per-operation values below. Being a 
public @AuxCounters
+    // field, JMH zeroes it automatically at the start of every iteration 
(which is why, unlike the
+    // private totals above, it is not reset in reset()).
+    public long operations;
+
+    // The divisor for the per-op methods below: (forks x measurement 
iterations). Set once per fork
+    // by captureRunShape().
+    private double measurementDataPoints = 1.0;
+
+    /**
+     * Captures the number of measurement data points — {@code forks x 
measurement iterations} — that
+     * JMH will SUM the {@code *PerOp()} methods over ({@code Type.EVENTS} 
secondary results are
+     * SUM-aggregated across iterations and forks). Each per-op method 
pre-divides by this count so that
+     * the SUM reports the exact per-operation value (e.g. {@code 
logReadsPerOp = 1.0}) in the summary
+     * row. Reading it from {@link BenchmarkParams} tracks the actual run 
shape (including
+     * {@code -f}/{@code -i} overrides) rather than hardcoding the annotation 
values.
+     */
+    @Setup(Level.Trial)
+    public void captureRunShape(BenchmarkParams params) {
+        if (params.getThreads() != 1) {
+            throw new IllegalStateException(
+                "raft benchmarks are single-threaded (one client over shared 
mocks); got "
+                    + params.getThreads() + " threads");
+        }
+        // forks() is 0 when forking is disabled (in-process), which is still 
one set of iterations.
+        int forks = Math.max(1, params.getForks());
+        measurementDataPoints = (double) forks * 
params.getMeasurement().getCount();
+    }
+
+    @Setup(Level.Iteration)
+    public void reset() {
+        logFlushesTotal = 0;
+        logReadsTotal = 0;

Review Comment:
   > Most likely they both will go through the OS page cache and not always hit 
the disk
   
   KRaft and raft-like replication is different from Kafka's normal topic data 
replication in that `fsync` is required to consider data as "written." 
Otherwise, the HWM of the metadata partition can decrease. Counting `log#flush` 
is our way of counting disk write IOs as a result of the test-writer/callers 
invoking `append`, since a new write always flushes to disk.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to