View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.xml;
20  
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.concurrent.TimeUnit;
25  
26  import org.apache.maven.api.xml.XmlNode;
27  import org.codehaus.plexus.configuration.PlexusConfiguration;
28  import org.openjdk.jmh.annotations.Benchmark;
29  import org.openjdk.jmh.annotations.BenchmarkMode;
30  import org.openjdk.jmh.annotations.Fork;
31  import org.openjdk.jmh.annotations.Measurement;
32  import org.openjdk.jmh.annotations.Mode;
33  import org.openjdk.jmh.annotations.OutputTimeUnit;
34  import org.openjdk.jmh.annotations.Scope;
35  import org.openjdk.jmh.annotations.Setup;
36  import org.openjdk.jmh.annotations.State;
37  import org.openjdk.jmh.annotations.Warmup;
38  import org.openjdk.jmh.infra.Blackhole;
39  
40  /**
41   * JMH benchmarks for measuring memory allocation patterns and garbage collection impact.
42   *
43   * This benchmark measures the memory efficiency improvements in the new implementation
44   * by creating many configuration objects and measuring allocation rates.
45   */
46  @BenchmarkMode(Mode.AverageTime)
47  @OutputTimeUnit(TimeUnit.MICROSECONDS)
48  @State(Scope.Benchmark)
49  @Fork(
50          value = 1,
51          jvmArgs = {"-XX:+UseG1GC", "-Xmx2g", "-Xms2g"})
52  @Warmup(iterations = 3, time = 2, timeUnit = TimeUnit.SECONDS)
53  @Measurement(iterations = 5, time = 3, timeUnit = TimeUnit.SECONDS)
54  public class XmlPlexusConfigurationMemoryBenchmark {
55  
56      private XmlNode smallNode;
57      private XmlNode mediumNode;
58      private XmlNode largeNode;
59  
60      @Setup
61      public void setup() {
62          smallNode = createSmallNode();
63          mediumNode = createMediumNode();
64          largeNode = createLargeNode();
65      }
66  
67      /**
68       * Benchmark memory allocation for small XML documents
69       */
70      @Benchmark
71      public List<PlexusConfiguration> memoryAllocationOldSmall() {
72          List<PlexusConfiguration> configs = new ArrayList<>();
73          // Create multiple configurations to measure allocation patterns
74          for (int i = 0; i < 100; i++) {
75              configs.add(new XmlPlexusConfigurationOld(smallNode));
76          }
77          return configs;
78      }
79  
80      @Benchmark
81      public List<PlexusConfiguration> memoryAllocationNewSmall() {
82          List<PlexusConfiguration> configs = new ArrayList<>();
83          // Create multiple configurations to measure allocation patterns
84          for (int i = 0; i < 100; i++) {
85              configs.add(new XmlPlexusConfiguration(smallNode));
86          }
87          return configs;
88      }
89  
90      /**
91       * Benchmark memory allocation for medium XML documents
92       */
93      @Benchmark
94      public List<PlexusConfiguration> memoryAllocationOldMedium() {
95          List<PlexusConfiguration> configs = new ArrayList<>();
96          for (int i = 0; i < 50; i++) {
97              configs.add(new XmlPlexusConfigurationOld(mediumNode));
98          }
99          return configs;
100     }
101 
102     @Benchmark
103     public List<PlexusConfiguration> memoryAllocationNewMedium() {
104         List<PlexusConfiguration> configs = new ArrayList<>();
105         for (int i = 0; i < 50; i++) {
106             configs.add(new XmlPlexusConfiguration(mediumNode));
107         }
108         return configs;
109     }
110 
111     /**
112      * Benchmark memory allocation for large XML documents
113      */
114     @Benchmark
115     public List<PlexusConfiguration> memoryAllocationOldLarge() {
116         List<PlexusConfiguration> configs = new ArrayList<>();
117         for (int i = 0; i < 10; i++) {
118             configs.add(new XmlPlexusConfigurationOld(largeNode));
119         }
120         return configs;
121     }
122 
123     @Benchmark
124     public List<PlexusConfiguration> memoryAllocationNewLarge() {
125         List<PlexusConfiguration> configs = new ArrayList<>();
126         for (int i = 0; i < 10; i++) {
127             configs.add(new XmlPlexusConfiguration(largeNode));
128         }
129         return configs;
130     }
131 
132     /**
133      * Benchmark lazy vs eager child creation impact on memory
134      */
135     @Benchmark
136     public void lazyVsEagerOld(Blackhole bh) {
137         PlexusConfiguration config = new XmlPlexusConfigurationOld(largeNode);
138         // All children are already created (eager), just access them
139         for (int i = 0; i < config.getChildCount(); i++) {
140             bh.consume(config.getChild(i));
141         }
142     }
143 
144     @Benchmark
145     public void lazyVsEagerNew(Blackhole bh) {
146         PlexusConfiguration config = new XmlPlexusConfiguration(largeNode);
147         // Children are created on-demand (lazy), measure the impact
148         for (int i = 0; i < config.getChildCount(); i++) {
149             bh.consume(config.getChild(i));
150         }
151     }
152 
153     /**
154      * Test memory sharing vs copying
155      */
156     @Benchmark
157     public PlexusConfiguration memorySharingOld() {
158         // This creates deep copies of all data
159         return new XmlPlexusConfigurationOld(largeNode);
160     }
161 
162     @Benchmark
163     public PlexusConfiguration memorySharingNew() {
164         // This shares the underlying XML structure
165         return new XmlPlexusConfiguration(largeNode);
166     }
167 
168     // Helper methods to create test nodes of different sizes
169     private XmlNode createSmallNode() {
170         Map<String, String> attrs = Map.of("id", "small-test");
171         List<XmlNode> children =
172                 List.of(XmlNode.newInstance("child1", "value1"), XmlNode.newInstance("child2", "value2"));
173 
174         return XmlNode.newBuilder()
175                 .name("small")
176                 .attributes(attrs)
177                 .children(children)
178                 .build();
179     }
180 
181     private XmlNode createMediumNode() {
182         Map<String, String> attrs = Map.of("id", "medium-test", "version", "1.0");
183         List<XmlNode> children = new ArrayList<>();
184 
185         for (int i = 0; i < 20; i++) {
186             Map<String, String> itemAttrs = Map.of("index", String.valueOf(i));
187             List<XmlNode> itemChildren = List.of(XmlNode.newInstance("nested" + i, "nested-value-" + i));
188 
189             children.add(XmlNode.newBuilder()
190                     .name("item" + i)
191                     .value("value-" + i)
192                     .attributes(itemAttrs)
193                     .children(itemChildren)
194                     .build());
195         }
196 
197         return XmlNode.newBuilder()
198                 .name("medium")
199                 .attributes(attrs)
200                 .children(children)
201                 .build();
202     }
203 
204     private XmlNode createLargeNode() {
205         Map<String, String> attrs = Map.of("id", "large-test", "version", "2.0", "type", "benchmark");
206         List<XmlNode> sections = new ArrayList<>();
207 
208         // Create a large, complex structure
209         for (int section = 0; section < 10; section++) {
210             Map<String, String> sectionAttrs = Map.of("name", "section-" + section);
211             List<XmlNode> items = new ArrayList<>();
212 
213             for (int item = 0; item < 20; item++) {
214                 Map<String, String> itemAttrs = Map.of("id", "item-" + section + "-" + item);
215                 List<XmlNode> nestedElements = new ArrayList<>();
216 
217                 // Add nested elements
218                 for (int nested = 0; nested < 5; nested++) {
219                     Map<String, String> nestedAttrs = Map.of("level", String.valueOf(nested));
220                     nestedElements.add(XmlNode.newBuilder()
221                             .name("nested" + nested)
222                             .value("nested-value-" + section + "-" + item + "-" + nested)
223                             .attributes(nestedAttrs)
224                             .build());
225                 }
226 
227                 items.add(XmlNode.newBuilder()
228                         .name("item" + item)
229                         .value("section-" + section + "-item-" + item)
230                         .attributes(itemAttrs)
231                         .children(nestedElements)
232                         .build());
233             }
234 
235             sections.add(XmlNode.newBuilder()
236                     .name("section" + section)
237                     .attributes(sectionAttrs)
238                     .children(items)
239                     .build());
240         }
241 
242         return XmlNode.newBuilder()
243                 .name("large")
244                 .attributes(attrs)
245                 .children(sections)
246                 .build();
247     }
248 }