XmlPlexusConfigurationBenchmark.java

/*
 * 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.maven.internal.xml;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.apache.maven.api.xml.XmlNode;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;

/**
 * JMH benchmarks comparing the performance of the old vs new XmlPlexusConfiguration implementations.
 *
 * To run these benchmarks:
 * mvn test-compile exec:java -Dexec.mainClass="org.openjdk.jmh.Main"
 *     -Dexec.classpathScope=test
 *     -Dexec.args="org.apache.maven.internal.xml.XmlPlexusConfigurationBenchmark"
 */
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Benchmark)
@Fork(1)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
public class XmlPlexusConfigurationBenchmark {

    private XmlNode simpleNode;
    private XmlNode complexNode;
    private XmlNode deepNode;

    @Setup
    public void setup() {
        // Create test XML nodes of varying complexity
        simpleNode = createSimpleNode();
        complexNode = createComplexNode();
        deepNode = createDeepNode();
    }

    /**
     * Benchmark constructor performance - Simple XML
     */
    @Benchmark
    public PlexusConfiguration constructorOldSimple() {
        return new XmlPlexusConfigurationOld(simpleNode);
    }

    @Benchmark
    public PlexusConfiguration constructorNewSimple() {
        return new XmlPlexusConfiguration(simpleNode);
    }

    /**
     * Benchmark constructor performance - Complex XML
     */
    @Benchmark
    public PlexusConfiguration constructorOldComplex() {
        return new XmlPlexusConfigurationOld(complexNode);
    }

    @Benchmark
    public PlexusConfiguration constructorNewComplex() {
        return new XmlPlexusConfiguration(complexNode);
    }

    /**
     * Benchmark constructor performance - Deep XML
     */
    @Benchmark
    public PlexusConfiguration constructorOldDeep() {
        return new XmlPlexusConfigurationOld(deepNode);
    }

    @Benchmark
    public PlexusConfiguration constructorNewDeep() {
        return new XmlPlexusConfiguration(deepNode);
    }

    /**
     * Benchmark child access performance - Lazy vs Eager
     */
    @Benchmark
    public void childAccessOldComplex(Blackhole bh) {
        PlexusConfiguration config = new XmlPlexusConfigurationOld(complexNode);
        // Access all children to measure eager loading performance
        for (int i = 0; i < config.getChildCount(); i++) {
            bh.consume(config.getChild(i));
        }
    }

    @Benchmark
    public void childAccessNewComplex(Blackhole bh) {
        PlexusConfiguration config = new XmlPlexusConfiguration(complexNode);
        // Access all children to measure lazy loading performance
        for (int i = 0; i < config.getChildCount(); i++) {
            bh.consume(config.getChild(i));
        }
    }

    /**
     * Benchmark memory allocation patterns
     */
    @Benchmark
    public PlexusConfiguration memoryAllocationOld() {
        // This will trigger deep copying and high memory allocation
        return new XmlPlexusConfigurationOld(deepNode);
    }

    @Benchmark
    public PlexusConfiguration memoryAllocationNew() {
        // This should have much lower memory allocation due to sharing
        return new XmlPlexusConfiguration(deepNode);
    }

    // Helper methods to create test XML nodes
    private XmlNode createSimpleNode() {
        Map<String, String> attrs = Map.of("attr1", "value1");
        return XmlNode.newBuilder()
                .name("simple")
                .value("test-value")
                .attributes(attrs)
                .build();
    }

    private XmlNode createComplexNode() {
        Map<String, String> attrs = Map.of("id", "test", "version", "1.0");
        List<XmlNode> children = List.of(
                XmlNode.newInstance("child1", "value1"),
                XmlNode.newInstance("child2", "value2"),
                XmlNode.newBuilder()
                        .name("child3")
                        .children(List.of(
                                XmlNode.newInstance("nested1", "nested-value1"),
                                XmlNode.newInstance("nested2", "nested-value2")))
                        .build(),
                XmlNode.newInstance("child4", "value4"),
                XmlNode.newInstance("child5", "value5"));

        return XmlNode.newBuilder()
                .name("complex")
                .attributes(attrs)
                .children(children)
                .build();
    }

    private XmlNode createDeepNode() {
        List<XmlNode> levels = new ArrayList<>();

        // Create a deep hierarchy to stress test performance
        for (int i = 0; i < 10; i++) {
            List<XmlNode> items = new ArrayList<>();
            for (int j = 0; j < 5; j++) {
                Map<String, String> itemAttrs = Map.of("index", String.valueOf(j));
                items.add(XmlNode.newBuilder()
                        .name("item" + j)
                        .value("value-" + i + "-" + j)
                        .attributes(itemAttrs)
                        .build());
            }
            levels.add(XmlNode.newBuilder().name("level" + i).children(items).build());
        }

        return XmlNode.newBuilder().name("root").children(levels).build();
    }
}