This is an automated email from the ASF dual-hosted git repository.

papegaaij pushed a commit to branch resource-name-iterator-alloc
in repository https://gitbox.apache.org/repos/asf/wicket.git

commit c67601968da3e0750fbdf01941c769e1ee5f501e
Author: Emond Papegaaij <[email protected]>
AuthorDate: Mon Sep 7 05:12:30 2026 +0000

    Benchmark writing a page to the page store
    
    SerializingPageStore turns every page into bytes at the end of a request, 
and
    CryptingPageStore encrypts the result when StoreSettings#isEncrypted() is 
on.
    Neither path had a benchmark.
    
    Both are parameterised by size rather than run on one shape, because that is
    what they scale with. Serialization tracks the number of objects in the 
graph -
    ObjectOutputStream keeps a handle table per stream and grows it as it 
walks, so
    a page of many small components costs more than its byte count suggests - 
while
    encryption tracks the payload, so anything it does per byte is invisible in 
a
    benchmark over short strings.
    
    Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---
 .../wicket/benchmarks/PageEncryptionBenchmark.java |  91 +++++++++++++++
 .../benchmarks/PageSerializationBenchmark.java     | 127 +++++++++++++++++++++
 2 files changed, 218 insertions(+)

diff --git 
a/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageEncryptionBenchmark.java
 
b/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageEncryptionBenchmark.java
new file mode 100644
index 0000000000..aef7d39073
--- /dev/null
+++ 
b/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageEncryptionBenchmark.java
@@ -0,0 +1,91 @@
+/*
+ * 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.wicket.benchmarks;
+
+import java.security.SecureRandom;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import javax.crypto.SecretKey;
+
+import org.apache.wicket.core.util.crypt.AesGcmCryptScheme;
+import org.apache.wicket.core.util.crypt.ICryptScheme;
+import org.apache.wicket.core.util.crypt.SchemeCrypt;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+import org.openjdk.jmh.annotations.Scope;
+import org.openjdk.jmh.annotations.Setup;
+import org.openjdk.jmh.annotations.State;
+import org.openjdk.jmh.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+
+/**
+ * Measures encrypting a serialized page, which {@code CryptingPageStore} does 
for every page when
+ * {@code StoreSettings#isEncrypted()} is on.
+ * <p>
+ * Sized like real pages rather than like a token: the payload is what gets 
copied, so anything the
+ * path does per byte shows up here and not in a benchmark over short strings.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Fork(3)
+@Threads(1)
+@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class PageEncryptionBenchmark
+{
+       @Param({"5000", "40000"})
+       public int payloadSize;
+
+       private SchemeCrypt crypt;
+
+       private byte[] plaintext;
+
+       private byte[] encrypted;
+
+       @Setup(Level.Trial)
+       public void setUp()
+       {
+               SecureRandom random = new SecureRandom();
+               ICryptScheme scheme = new AesGcmCryptScheme();
+               SecretKey key = scheme.generateKey(random);
+               crypt = new SchemeCrypt(key, random, scheme, List.of(scheme));
+
+               plaintext = new byte[payloadSize];
+               random.nextBytes(plaintext);
+               encrypted = crypt.encrypt(plaintext);
+       }
+
+       @Benchmark
+       public byte[] encrypt()
+       {
+               return crypt.encrypt(plaintext);
+       }
+
+       @Benchmark
+       public byte[] decrypt()
+       {
+               return crypt.decrypt(encrypted);
+       }
+}
diff --git 
a/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageSerializationBenchmark.java
 
b/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageSerializationBenchmark.java
new file mode 100644
index 0000000000..1445ebbf9e
--- /dev/null
+++ 
b/wicket-benchmarks/src/main/java/org/apache/wicket/benchmarks/PageSerializationBenchmark.java
@@ -0,0 +1,127 @@
+/*
+ * 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.wicket.benchmarks;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.wicket.Component;
+import org.apache.wicket.MarkupContainer;
+import org.apache.wicket.markup.IMarkupResourceStreamProvider;
+import org.apache.wicket.markup.html.WebMarkupContainer;
+import org.apache.wicket.markup.html.WebPage;
+import org.apache.wicket.markup.html.basic.Label;
+import org.apache.wicket.markup.html.list.ListItem;
+import org.apache.wicket.markup.html.list.ListView;
+import org.apache.wicket.markup.repeater.RepeatingView;
+import org.apache.wicket.mock.MockApplication;
+import org.apache.wicket.model.Model;
+import org.apache.wicket.serialize.java.JavaSerializer;
+import org.apache.wicket.util.resource.IResourceStream;
+import org.apache.wicket.util.resource.StringResourceStream;
+import org.apache.wicket.util.tester.WicketTester;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Level;
+import org.openjdk.jmh.annotations.Measurement;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+import org.openjdk.jmh.annotations.Param;
+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.annotations.Threads;
+import org.openjdk.jmh.annotations.Warmup;
+
+/**
+ * Measures writing a page to bytes, which is what {@code 
SerializingPageStore} does for every page
+ * at the end of a request.
+ * <p>
+ * Parameterised by component count because the cost tracks the number of 
objects in the graph
+ * rather than the size of the result: {@code ObjectOutputStream} keeps a 
handle table per stream
+ * and grows it as it walks, so a page of many small components costs more 
than its byte count
+ * suggests.
+ * <p>
+ * {@code -prof gc} is the point of this one. {@code gc.alloc.rate.norm} 
covers both the serialized
+ * bytes and everything the stream allocated to produce them, so read it 
against the result size:
+ * 50 components serialize to about 5kB, 500 components to about 37kB.
+ */
+@BenchmarkMode(Mode.AverageTime)
+@OutputTimeUnit(TimeUnit.MICROSECONDS)
+@Fork(3)
+@Threads(1)
+@Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
+@Measurement(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS)
+@State(Scope.Thread)
+public class PageSerializationBenchmark
+{
+       @Param({"50", "500"})
+       public int components;
+
+       private WicketTester tester;
+
+       private JavaSerializer serializer;
+
+       private WebPage page;
+
+       @Setup(Level.Trial)
+       public void setUp()
+       {
+               tester = new WicketTester(new MockApplication());
+               serializer = new JavaSerializer("benchmarks");
+               page = new BenchmarkPage(components);
+               tester.startPage(page);
+       }
+
+       @TearDown(Level.Trial)
+       public void tearDown()
+       {
+               tester.destroy();
+       }
+
+       @Benchmark
+       public byte[] serializePage()
+       {
+               return serializer.serialize(page);
+       }
+
+       private static class BenchmarkPage extends WebPage implements 
IMarkupResourceStreamProvider
+       {
+               private static final long serialVersionUID = 1L;
+
+               BenchmarkPage(int components)
+               {
+                       RepeatingView view = new RepeatingView("rv");
+                       for (int i = 0; i < components; i++)
+                       {
+                               WebMarkupContainer child = new 
WebMarkupContainer(view.newChildId());
+                               child.add(new Label("label", Model.of("value " 
+ i)));
+                               view.add(child);
+                       }
+                       add(view);
+               }
+
+               @Override
+               public IResourceStream getMarkupResourceStream(MarkupContainer 
container,
+                       Class< ? > containerClass)
+               {
+                       return new StringResourceStream(
+                               "<html><body><div wicket:id=\"rv\"><span 
wicket:id=\"label\"></span></div></body></html>");
+               }
+       }
+}

Reply via email to