| /* |
| * 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.logging.log4j.perf.jmh; |
| |
| import java.util.Arrays; |
| import java.util.concurrent.TimeUnit; |
| |
| import org.apache.logging.log4j.message.ParameterizedMessage; |
| import org.openjdk.jmh.annotations.Benchmark; |
| import org.openjdk.jmh.annotations.BenchmarkMode; |
| import org.openjdk.jmh.annotations.Mode; |
| import org.openjdk.jmh.annotations.OutputTimeUnit; |
| import org.openjdk.jmh.annotations.Scope; |
| import org.openjdk.jmh.annotations.State; |
| |
| /** |
| */ |
| // ============================== HOW TO RUN THIS TEST: ==================================== |
| // |
| // single thread: |
| // java -jar log4j-perf/target/benchmarks.jar ".*ParameterizedMessageInlining.*" -f 1 -wi 5 -i 10 |
| // |
| // multiple threads (for example, 4 threads): |
| // java -jar log4j-perf/target/benchmarks.jar ".*ParameterizedMessageInlining.*" -f 1 -wi 5 -i 10 -t 4 -si true |
| // |
| // Usage help: |
| // java -jar log4j-perf/target/benchmarks.jar -help |
| // |
| @State(Scope.Benchmark) |
| public class ParameterizedMessageInliningBenchmark { |
| private static final char DELIM_START = '{'; |
| private static final char DELIM_STOP = '}'; |
| private static final char ESCAPE_CHAR = '\\'; |
| private static final String[] ARGS = { "arg1", "arg2" }; |
| |
| public static void main(final String[] args) { |
| } |
| |
| @Benchmark |
| @BenchmarkMode(Mode.SampleTime) |
| @OutputTimeUnit(TimeUnit.NANOSECONDS) |
| public String format() { |
| return format("pattern {} with {} two parameters and some text", ARGS); |
| } |
| |
| @Benchmark |
| @BenchmarkMode(Mode.SampleTime) |
| @OutputTimeUnit(TimeUnit.NANOSECONDS) |
| public String format0() { |
| return format0("pattern {} with {} two parameters and some text", ARGS); |
| } |
| |
| @Benchmark |
| @BenchmarkMode(Mode.SampleTime) |
| @OutputTimeUnit(TimeUnit.NANOSECONDS) |
| public String formatStringArgs() { |
| return ParameterizedMessage.format("pattern {} with {} two parameters and some text", ARGS); |
| } |
| |
| @Benchmark |
| @BenchmarkMode(Mode.SampleTime) |
| @OutputTimeUnit(TimeUnit.NANOSECONDS) |
| public String format0_inlined2() { |
| return format0_inlined2("pattern {} with {} two parameters and some text", ARGS); |
| } |
| public static String format(final String messagePattern, final Object[] arguments) { |
| if (messagePattern == null || arguments == null || arguments.length == 0) { |
| return messagePattern; |
| } |
| |
| final StringBuilder result = new StringBuilder(); |
| int escapeCounter = 0; |
| int currentArgument = 0; |
| for (int i = 0; i < messagePattern.length(); i++) { |
| final char curChar = messagePattern.charAt(i); |
| if (curChar == ESCAPE_CHAR) { |
| escapeCounter++; |
| } else { |
| if (curChar == DELIM_START && i < messagePattern.length() - 1 |
| && messagePattern.charAt(i + 1) == DELIM_STOP) { |
| // write escaped escape chars |
| final int escapedEscapes = escapeCounter / 2; |
| for (int j = 0; j < escapedEscapes; j++) { |
| result.append(ESCAPE_CHAR); |
| } |
| |
| if (escapeCounter % 2 == 1) { |
| // i.e. escaped |
| // write escaped escape chars |
| result.append(DELIM_START); |
| result.append(DELIM_STOP); |
| } else { |
| // unescaped |
| if (currentArgument < arguments.length) { |
| result.append(arguments[currentArgument]); |
| } else { |
| result.append(DELIM_START).append(DELIM_STOP); |
| } |
| currentArgument++; |
| } |
| i++; |
| escapeCounter = 0; |
| continue; |
| } |
| // any other char beside ESCAPE or DELIM_START/STOP-combo |
| // write unescaped escape chars |
| if (escapeCounter > 0) { |
| for (int j = 0; j < escapeCounter; j++) { |
| result.append(ESCAPE_CHAR); |
| } |
| escapeCounter = 0; |
| } |
| result.append(curChar); |
| } |
| } |
| return result.toString(); |
| } |
| |
| // 259 bytes |
| public static String format0(final String messagePattern, final String[] arguments) { |
| if (messagePattern == null || arguments == null || arguments.length == 0) { |
| return messagePattern; |
| } |
| |
| final int len = messagePattern.length(); |
| final char[] result = new char[len + sumStringLengths(arguments)]; |
| int pos = 0; |
| int escapeCounter = 0; |
| int currentArgument = 0; |
| for (int i = 0; i < len; i++) { |
| final char curChar = messagePattern.charAt(i); |
| if (curChar == ESCAPE_CHAR) { |
| escapeCounter++; |
| } else { |
| if (curChar == DELIM_START && i < len - 1 && messagePattern.charAt(i + 1) == DELIM_STOP) { |
| // write escaped escape chars |
| int escapedEscapes = escapeCounter >> 1; // divide by 2 |
| while (escapedEscapes > 0) { |
| result[pos++] = ESCAPE_CHAR; |
| escapedEscapes--; |
| } |
| |
| if ((escapeCounter & 1) == 1) { |
| // i.e. escaped |
| // write escaped escape chars |
| result[pos++] = DELIM_START; |
| result[pos++] = DELIM_STOP; |
| } else { |
| // unescaped |
| if (currentArgument < arguments.length) { |
| final String arg = arguments[currentArgument]; |
| final int argLen = arg.length(); |
| arg.getChars(0, argLen, result, pos); |
| pos += argLen; |
| } else { |
| result[pos++] = DELIM_START; |
| result[pos++] = DELIM_STOP; |
| } |
| currentArgument++; |
| } |
| i++; |
| escapeCounter = 0; |
| continue; |
| } |
| // any other char beside ESCAPE or DELIM_START/STOP-combo |
| // write unescaped escape chars |
| while (escapeCounter > 0) { |
| result[pos++] = ESCAPE_CHAR; |
| escapeCounter--; |
| } |
| result[pos++] = curChar; |
| } |
| } |
| return Arrays.toString(result); |
| } |
| |
| // 33 bytes |
| public static String format0_inlined2(final String messagePattern, final String[] arguments) { |
| int len = 0; |
| if (messagePattern == null || (len = messagePattern.length()) == 0 || arguments == null |
| || arguments.length == 0) { |
| return messagePattern; |
| } |
| |
| return format0_inlined22(messagePattern, len, arguments); |
| } |
| |
| // 157 bytes |
| private static String format0_inlined22(final String messagePattern, final int len, final String[] arguments) { |
| final char[] result = new char[len + sumStringLengths(arguments)]; |
| int pos = 0; |
| int escapeCounter = 0; |
| int currentArgument = 0; |
| int i = 0; |
| for (; i < len - 1; i++) { |
| final char curChar = messagePattern.charAt(i); |
| if (curChar == ESCAPE_CHAR) { |
| escapeCounter++; |
| } else { |
| if (isDelimPair(messagePattern, i, curChar)) { |
| i++; |
| |
| // write escaped escape chars |
| pos = format0_writeEscapedEscapeChars(escapeCounter, result, pos); |
| |
| if (isOdd(escapeCounter)) { |
| // i.e. escaped |
| // write escaped escape chars |
| pos = format0_writeDelimPair(result, pos); |
| } else { |
| // unescaped |
| pos = format0_appendArg(arguments, currentArgument, result, pos); |
| currentArgument++; |
| } |
| } else { |
| pos = format0_handleLiteralChar(result, pos, escapeCounter, curChar); |
| } |
| escapeCounter = 0; |
| } |
| } |
| pos = format0_handleMaybeLastChar(messagePattern, len, result, pos, escapeCounter, i); |
| return new String(result, 0, pos); |
| } |
| |
| /** |
| * Returns the sum of the lengths of all Strings in the specified array. |
| */ |
| // 27 bytes |
| private static int sumStringLengths(final String[] arguments) { |
| int result = 0; |
| for (int i = 0; i < arguments.length; i++) { |
| result += arguments[i].length(); |
| } |
| return result; |
| } |
| |
| // 22 bytes |
| private static boolean isDelimPair(final String messagePattern, final int i, final char curChar) { |
| return curChar == DELIM_START && messagePattern.charAt(i + 1) == DELIM_STOP; |
| } |
| |
| // 28 bytes |
| private static int format0_handleMaybeLastChar(final String messagePattern, final int len, final char[] result, |
| int pos, final int escapeCounter, final int i) { |
| if (i == len - 1) { |
| final char curChar = messagePattern.charAt(i); |
| pos = format0_handleLastChar(result, pos, escapeCounter, curChar); |
| } |
| return pos; |
| } |
| |
| // 28 bytes |
| private static int format0_handleLastChar(final char[] result, int pos, final int escapeCounter, final char curChar) { |
| if (curChar == ESCAPE_CHAR) { |
| pos = format0_writeUnescapedEscapeChars(escapeCounter + 1, result, pos); |
| } else { |
| pos = format0_handleLiteralChar(result, pos, escapeCounter, curChar); |
| } |
| return pos; |
| } |
| |
| // 16 bytes |
| private static int format0_handleLiteralChar(final char[] result, int pos, final int escapeCounter, final char curChar) { |
| // any other char beside ESCAPE or DELIM_START/STOP-combo |
| // write unescaped escape chars |
| pos = format0_writeUnescapedEscapeChars(escapeCounter, result, pos); |
| result[pos++] = curChar; |
| return pos; |
| } |
| |
| // 18 bytes |
| private static int format0_writeDelimPair(final char[] result, int pos) { |
| result[pos++] = DELIM_START; |
| result[pos++] = DELIM_STOP; |
| return pos; |
| } |
| |
| /** |
| * Returns {@code true} if the specified parameter is odd. |
| */ |
| // 11 bytes |
| private static boolean isOdd(final int number) { |
| return (number & 1) == 1; |
| } |
| |
| // 11 bytes |
| private static int format0_writeEscapedEscapeChars(final int escapeCounter, final char[] result, final int pos) { |
| final int escapedEscapes = escapeCounter >> 1; // divide by two |
| return format0_writeUnescapedEscapeChars(escapedEscapes, result, pos); |
| } |
| |
| // 20 bytes |
| private static int format0_writeUnescapedEscapeChars(int escapeCounter, final char[] result, int pos) { |
| while (escapeCounter > 0) { |
| result[pos++] = ESCAPE_CHAR; |
| escapeCounter--; |
| } |
| return pos; |
| } |
| |
| // 25 bytes |
| private static int format0_appendArg(final String[] arguments, final int currentArgument, final char[] result, int pos) { |
| if (currentArgument < arguments.length) { |
| pos = format0_appendArg0(arguments, currentArgument, result, pos); |
| } else { |
| pos = format0_writeDelimPair(result, pos); |
| } |
| return pos; |
| } |
| |
| // 27 bytes |
| private static int format0_appendArg0(final String[] arguments, final int currentArgument, final char[] result, final int pos) { |
| final String arg = arguments[currentArgument]; |
| final int argLen = arg.length(); |
| arg.getChars(0, argLen, result, pos); |
| return pos + argLen; |
| } |
| } |