view src/main/java/org/openjdk/gcbench/tests/DimensionalTest.java @ 89:d68c66d67740

Rehash gathered safepoints and GC pauses metrics. Formatting changes.
author shade
date Thu, 23 Nov 2017 15:10:59 +0100
parents 583fef4276f5
children 14c1bb4faa6e
line wrap: on
line source

/*
 * Copyright (c) 2017, Red Hat Inc. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package org.openjdk.gcbench.tests;

import org.openjdk.jmh.profile.GCProfiler;
import org.openjdk.jmh.profile.SafepointsProfiler;
import org.openjdk.jmh.results.Result;
import org.openjdk.jmh.results.RunResult;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.*;

import java.util.Map;

public class DimensionalTest extends AbstractTest {
    private final Dimension[] dimensions;
    private final boolean rated;

    public DimensionalTest(Options baseOpts, Class<?> benchmark, String label, String description,
                           Dimension... dimensions) {
        this(baseOpts, benchmark, label, description, false, dimensions);
    }

    public DimensionalTest(Options baseOpts, Class<?> benchmark, String label, String description,
                           boolean rated,
                           Dimension... dimensions) {
        super(baseOpts, label, benchmark, description);
        this.dimensions = dimensions;
        this.rated = rated;
    }

    @Override
    protected void doRun() {
        pw.println("  Test dimensions: ");
        for (Dimension d : dimensions) {
            pw.print("    ");
            pw.println(d.toString());
        }
        pw.println();

        pw.print("  ");
        for (Dimension d : dimensions) {
            pw.printf("%-10s ", d.label());
        }
        if (rated) {
            pw.printf("%-15s ", "Target rate");
        }

        pw.printf("%-25s %-25s   %-7s      %-46s      %-46s%n",
                "Work rate",
                "Allocation rate",
                "Pauses",
                "Pause time",
                "Time to safepoint"
        );

        pw.print("  ");
        for (Dimension d : dimensions) {
            pw.printf("%-10s ", "");
        }
        if (rated) {
            pw.printf("%-15s ", "");
        }

        pw.printf("%-25s %-25s   %-7s     %8s %12s %12s %12s     %8s %12s %12s %12s%n",
                "",
                "",
                "",
                "total %", "avg", "99.9%", "max",
                "pause %", "avg", "99.9%", "max"
        );

        Object lastKeyVal = null;

        for (DimensionValues values : DimensionValues.product(dimensions)) {
            ChainedOptionsBuilder builder = new OptionsBuilder()
                    .parent(baseOpts)
                    .addProfiler(GCProfiler.class)
                    .addProfiler(SafepointsProfiler.class)
                    .include(benchmark.getName());

            int heapSize = -1;
            int lds = -1;

            Object keyVal = -1;
            for (int i = 0; i < dimensions.length; i++) {
                Dimension d = dimensions[i];
                Object value = values.values[i];
                if (i == dimensions.length - 2) {
                    keyVal = value;
                }

                int intValue = Integer.valueOf(value.toString());

                switch (d.type()) {
                    case HEAPSIZE:
                        builder = builder.jvmArgsPrepend("-Xmx" + value + "m", "-Xms" + value + "m");
                        heapSize = intValue;
                        break;
                    case LDS:
                        builder = builder.param("ldsMB", String.valueOf(value));
                        lds = intValue;
                        break;
                    case THREADS:
                        builder = builder.threads(intValue);
                        break;
                    case SIZE:
                        builder = builder.param("size", String.valueOf(value));
                        break;
                    default:
                        throw new IllegalStateException("Unknown dimension: " + d);
                }
            }

            if (heapSize != -1 && lds != -1 && lds >= heapSize) {
                // Skip this one!
                continue;
            }

            if (lastKeyVal != keyVal) {
                pw.println();
                lastKeyVal = keyVal;
            }

            if (rated) {
                final int RATE_STEPS = 5;
                int maxRate = calibrateRate(builder.build());
                for (int rate = maxRate / RATE_STEPS; rate <= maxRate; rate += Math.max(1, maxRate / RATE_STEPS)) {
                    Options opts = new OptionsBuilder()
                            .parent(builder.build())
                            .param("rate", String.valueOf(rate))
                            .build();
                    runWith(values, opts, rate);
                }
                pw.println();
            } else {
                runWith(values, builder.build(), 0);
            }
        }
    }

    private int calibrateRate(Options base) {
        try {
            Options opts = new OptionsBuilder()
                    .parent(base)
                    .param("rate", String.valueOf(Integer.MAX_VALUE))
                    .build();

            RunResult result = new Runner(opts).runSingle();
            return (int) result.getPrimaryResult().getScore();
        } catch (RunnerException e) {
            return 0;
        }
    }

    private void runWith(DimensionValues values, Options opts, int rate) {
        pw.print("  ");

        for (Object v : values.values) {
            pw.printf("%-10s ", v);
        }

        if (rated) {
            pw.printf("%-15s ", rate);
        }

        try {
            RunResult result = new Runner(opts).runSingle();

            Result prim = result.getPrimaryResult();
            Map<String, Result> sec = result.getSecondaryResults();

            pw.printf("%-25s %-25s   %-7s     %8s %12s %12s %12s     %8s %12s %12s %12s%n",
                    shortVal(prim),
                    shortVal(sec.get("·gc.alloc.rate")),
                    intVal(sec.get("·safepoints.pause.count")),
                    percentRatioVal(sec.get("·safepoints.pause"), sec.get("·safepoints.interval")),
                    latencyVal(sec.get("·safepoints.pause.avg")),
                    latencyVal(sec.get("·safepoints.pause.p0.999")),
                    latencyVal(sec.get("·safepoints.pause.p1.00")),
                    percentRatioVal(sec.get("·safepoints.ttsp"), sec.get("·safepoints.pause")),
                    latencyVal(sec.get("·safepoints.ttsp.avg")),
                    latencyVal(sec.get("·safepoints.ttsp.p0.999")),
                    latencyVal(sec.get("·safepoints.ttsp.p1.00"))
            );
        } catch (RunnerException e) {
            pw.print("FAILED: ");
            Throwable cause = e.getCause();
            if (cause != null) {
                for (Throwable t : cause.getSuppressed()) {
                    pw.print(t.getMessage() + " ");
                }
            } else {
                pw.println(" unknown error");
                e.printStackTrace(pw);
            }
            pw.println();
        }
    }

    private String percentRatioVal(Result r1, Result r2) {
        if (r1 != null && r2 != null) {
            return String.format("%.2f %%", 100D * r1.getScore() / r2.getScore());
        }
        return "-";
    }

    private String intVal(Result r) {
        if (r != null) {
            if (!Double.isNaN(r.getScoreError())) {
                return String.format("%d ± %d", Math.round(r.getScore()), Math.round(r.getScoreError()));
            } else {
                return String.format("%d", Math.round(r.getScore()));
            }
        } else {
            return "-";
        }
    }

    private String shortVal(Result r) {
        if (r != null) {
            if (!Double.isNaN(r.getScoreError())) {
                return String.format("%.1f ± %.1f %s", r.getScore(), r.getScoreError(), r.getScoreUnit());
            } else {
                return String.format("%.1f %s", r.getScore(), r.getScoreUnit());
            }
        } else {
            return "-";
        }
    }

    private String latencyVal(Result r) {
        if (r != null && !Double.isNaN(r.getScore())) {
            return String.format("%.3f %s", r.getScore(), r.getScoreUnit());
        } else {
            return "-";
        }
    }

}