This is an automated email from the ASF dual-hosted git repository. markt pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
The following commit(s) were added to refs/heads/11.0.x by this push: new 92ec31c806 Fix BZ 69285. Optimize creation of ParameterMap from ParameterMap 92ec31c806 is described below commit 92ec31c806d15cd5509c62152e9b830396d5cbfa Author: Mark Thomas <ma...@apache.org> AuthorDate: Fri Aug 30 11:27:29 2024 +0100 Fix BZ 69285. Optimize creation of ParameterMap from ParameterMap Based on sample code and test cases provided by John Engebretson --- .../catalina/core/ApplicationHttpRequest.java | 3 +- java/org/apache/catalina/util/ParameterMap.java | 13 +++ .../catalina/util/TestParameterMapPerformance.java | 102 +++++++++++++++++++++ webapps/docs/changelog.xml | 5 + 4 files changed, 121 insertions(+), 2 deletions(-) diff --git a/java/org/apache/catalina/core/ApplicationHttpRequest.java b/java/org/apache/catalina/core/ApplicationHttpRequest.java index 3438a4bc0e..3b512c30db 100644 --- a/java/org/apache/catalina/core/ApplicationHttpRequest.java +++ b/java/org/apache/catalina/core/ApplicationHttpRequest.java @@ -713,8 +713,7 @@ class ApplicationHttpRequest extends HttpServletRequestWrapper { return; } - parameters = new ParameterMap<>(); - parameters.putAll(getRequest().getParameterMap()); + parameters = new ParameterMap<>(getRequest().getParameterMap()); mergeParameters(); ((ParameterMap<String,String[]>) parameters).setLocked(true); parsedParams = true; diff --git a/java/org/apache/catalina/util/ParameterMap.java b/java/org/apache/catalina/util/ParameterMap.java index 208893ab2d..38515e3460 100644 --- a/java/org/apache/catalina/util/ParameterMap.java +++ b/java/org/apache/catalina/util/ParameterMap.java @@ -87,6 +87,19 @@ public final class ParameterMap<K, V> implements Map<K,V>, Serializable { } + /** + * Optimised constructor for ParameterMap. + * + * @see "https://bz.apache.org/bugzilla/show_bug.cgi?id=69285" + * + * @param map Map whose contents are duplicated in the new map + */ + public ParameterMap(ParameterMap<K,V> map) { + delegatedMap = new LinkedHashMap<>(map.delegatedMap); + unmodifiableDelegatedMap = Collections.unmodifiableMap(delegatedMap); + } + + /** * The current lock state of this parameter map. */ diff --git a/test/org/apache/catalina/util/TestParameterMapPerformance.java b/test/org/apache/catalina/util/TestParameterMapPerformance.java new file mode 100644 index 0000000000..a9bf7e9f34 --- /dev/null +++ b/test/org/apache/catalina/util/TestParameterMapPerformance.java @@ -0,0 +1,102 @@ +/* + * 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.catalina.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.junit.Assert; +import org.junit.Test; + +public class TestParameterMapPerformance { + + private final int numTests = 5; + private final int numTestIterations = 1000000; + + private ParameterMap<Integer,Object> baseParams; + private ParameterMap<Integer,Object> testParams; + + @Test + public void testCompareStandardAndOptimizedMapConstructor() { + Map<Integer,Object> values = new HashMap<>(); + for (int i = 0; i < 50; i++) { + Integer integer = Integer.valueOf(i); + values.put(integer, integer); + } + + baseParams = new ParameterMap<>(values); + baseParams.setLocked(true); + + // warmup + for (int i = 0; i < numTestIterations; i++) { + useStandardConstructor(); + useOptimizedMapConstructor(); + } + + List<Long> standardDurations = new ArrayList<>(); + for (int i = 0; i < numTests; i++) { + System.gc(); + long startStandard = System.currentTimeMillis(); + for (int j = 0; j < numTestIterations; j++) { + useStandardConstructor(); + } + long duration = System.currentTimeMillis() - startStandard; + standardDurations.add(Long.valueOf(duration)); + System.out.println("Done with standard in " + duration + "ms"); + } + /* + * The CI systems tend to produce outliers that lead to false failures so skip the longest two runs. + */ + long standardTotalDuration = + standardDurations.stream().sorted().limit(numTests - 2).reduce(Long::sum).get().longValue(); + + List<Long> optimizedDurations = new ArrayList<>(); + for (int i = 0; i < numTests; i++) { + System.gc(); + long startOptimized = System.currentTimeMillis(); + for (int j = 0; j < numTestIterations; j++) { + useOptimizedMapConstructor(); + } + long duration = System.currentTimeMillis() - startOptimized; + optimizedDurations.add(Long.valueOf(duration)); + System.out.println("Done with optimized in " + duration + "ms"); + } + /* + * The CI systems tend to produce outliers that lead to false failures so skip the longest two runs. + */ + long optimizedTotalDuration = + optimizedDurations.stream().sorted().limit(numTests - 2).reduce(Long::sum).get().longValue(); + + Assert.assertTrue("Standard: " + standardTotalDuration + "ms, Optimized: " + optimizedTotalDuration + "ms", + optimizedTotalDuration < standardTotalDuration); + } + + + private void useOptimizedMapConstructor() { + testParams = new ParameterMap<>(baseParams); + testParams.entrySet(); + } + + + private void useStandardConstructor() { + testParams = new ParameterMap<>(); + testParams.putAll(baseParams); + testParams.entrySet(); + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index 0f0f4a45ec..d2366f1bed 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -113,6 +113,11 @@ written to the response, further writes should trigger an <code>IOException</code>. (markt) </add> + <fix> + Improve performance of + <code>ApplicationHttpRequest.parseParameters()</code>. Based on sample + code and test cases provided by John Engebretson. (markt) + </fix> </changelog> </subsection> <subsection name="Coyote"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org