JMH alternatives : REPL time, linux perf

Expanding on previous articles about measuring with JMH

Besides using the calls to System nanoseconds and millis …
There exist some tools for a quick feedback about the performance of your java JVM code.

ON ALL JVM
You can use the time command in the REPL
class Nothing {
public static void main(String[] args) {
}
}

Compile with:
> javac Nothing.java

and run:

> time java Nothing

real 0m0.104s
user 0m0.079s
sys 0m0.019s

ON LINUX UNIX
It is better to use perf then. You can run the command several times. You can also choose among many different categories of statistics. You need to be root.This is an example which runs the program 100 times:

sudo perf stat -e cpu-clock -r50 java HeeloWorld
Hello World! # printed 100x

Performance counter stats for ‘java HeeloWorld’ (100 runs):

190.546723 cpu-clock (msec) # 0.992 CPUs utilized ( +- 0.91% )

0.265467836 seconds time elapsed ( +- 1.49% )

ArrayList algorithm complexity, performance and gotchas

This piece of advice is about the Java ArrayList and LinkedList. It is not a insight about a general not specified List algorithm.

ArrayList is a random access data store.

LinkedList is sequential access data store.

There are these very important performance issues to know:

  • Remove: ArrayList remove(E ) is painful slow. Please consider LinkedList if this operation is used often in your application. Also the ArrayList positional remove(index) is painful bad. These operations require constant-time in a LinkedList and linear-time in an ArrayList. (*)
  • Use ArrayList constructor that take a size argument. You can measure by your self the defference when using large lists.
  • List Interface : Iterating over the elements in a list is preferable to indexing through it if the caller does not know the implementation.
  • Remember that you have the method addAll() for bulk operations.
  • Add positional add(index) is painful O(n) on ArrayList and O(1) in LinkedList (*)
  • Set positional set(index) is painful O(n) on LinkedList and O(1) in ArrayList   (*)

(*) Also the outline of the Collections Framework states that LinkedList is better than ArrayList for accessing in the middle of the list.

Also:

Do not do this:

 int a1[] = { -23, 12, 21 };
 
for (int v : a1)
           myarraylist.add(new Integer(v));

prefer this instead:

myarrayList.addAll(Arrays.asList(23,12,21)

Also

Arrays.asList

for translating arrays to strings is obsolete. Use this instead:

System.out.println(Arrays.toString(myArray));

Static Lock (ReentrantLock) and SonarQube destroyed my day

Yes, the idea of letting a module pass through a SonarQube validation throw us in putting ‘static’ on a lot of fields. A static happen to be attached to a ReentrantLock …
And many wasted hours of debugging followed because the applications using this module became crazy and not correct.
This piece of code can destry your application:

private static final Lock = new ReentrantLock();

Benchmark , Performance StringJoiner StringBuffer “+” plus operator

This is a benchmark for performance and behaviour of joining strings with:

  • StringJoiner
  • StringBuffer
  • The + operator

The result is that

StringJoiner is the winner.

StringBuffer and ‘+’ are worse and have the same performance.

package org.sample;
 
import java.util.ArrayList;
import java.util.List;
 
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
 
@Warmup(iterations = 10, time = 1)
@Measurement(iterations = 10, time = 1)
@State(Scope.Benchmark)
@Fork(1)
public class JoinString extends JoinStringBase {
	@Param({ "10000" })
	private int CYCLES;
 
	private final int P = 4;
 
	private List listCounter;
 
	private List listA;
	private List listC;
	private List listD;
	private List listXP;
	private List listJOINER;
 
	@Setup(Level.Trial)
	public void setUp() {
		listCounter = new ArrayList<>(CYCLES + 1);
 
		for (int i = 0; i < CYCLES; i++) {
			listCounter.add("" + i);
		}
 
		listA = new ArrayList<>(CYCLES + 1);
		listC = new ArrayList<>(CYCLES + 1);
		listD = new ArrayList<>(CYCLES + 1);
		listXP = new ArrayList<>(CYCLES + 1);
		listJOINER = new ArrayList<>(CYCLES + 1);
 
		System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", Integer.toString(P));
	}
 
	@Benchmark
	public long useStringBuffer() {
		for (int i = 0; i < CYCLES; i++) {
			listC.add(bufferDateString(TEXT));
		}
		return listC.size();
	}
 
	@Benchmark
	public long plusOperator() {
		for (int i = 0; i < CYCLES; i++) {
			listA.add(plusOperator(TEXT));
		}
		return listA.size();
	}
 
	@Benchmark
	public long useStringJoiner() {
		for (int i = 0; i < CYCLES; i++) {
			listJOINER.add(stringJoiner(TEXT));
		}
		return listJOINER.size();
	}
}
 
 
 
import java.util.StringJoiner;
 
class JoinStringBase {
 
	private static final String TWENTY = "20";
	private static final String POINT = ".";
 
	protected static final String TEXT = "_20ge20";
 
	String stringJoiner(String pScad) {
		StringJoiner sj = new StringJoiner("");
		String day = pScad.substring(1, 3);
		String month = pScad.substring(3, 5);
		String monthInt = MONTHS.valueOf(month).month;
		String yearSuffix = pScad.substring(5, 7);
 
		sj.add(day);
		sj.add(POINT);
		sj.add(monthInt);
		sj.add(POINT);
		sj.add(TWENTY);
		sj.add(yearSuffix);
		return sj.toString();
 
	}
 
	final String bufferDateString(String pScad) {
		StringBuffer buffer = new StringBuffer();
		String day = pScad.substring(1, 3);
		String month = pScad.substring(3, 5);
		String monthInt = MONTHS.valueOf(month).month;
		String yearSuffix = pScad.substring(5, 7);
 
		buffer.append(day);
		buffer.append(POINT);
 
		buffer.append(monthInt);
		buffer.append(POINT);
 
		buffer.append(TWENTY);
		buffer.append(yearSuffix);
		return buffer.toString();
	}
 
	final String plusOperator(String pScad) {
		String day = pScad.substring(1, 3);
		String month = pScad.substring(3, 5);
		String monthInt = MONTHS.valueOf(month).month;
		String yearSuffix = pScad.substring(5, 7);
 
		String scadFormat = day + POINT + monthInt + POINT + TWENTY + yearSuffix;
		return scadFormat;
	}
 
	enum MONTHS {
		ge("01"), fb("02"), mz("03"), ap("04"), mg("05"), gn("06"), lg("07"), ag("08"), st("09"), ot("10"), nv("11"), dc("12");
 
		final String month;
 
		MONTHS(String month) {
			this.month = month;
		}
	}
 
	final String plusOperatorXP(String pScad) {
		return pScad.substring(1, 3) + POINT + MONTHS.valueOf(pScad.substring(3, 5)).month + POINT + TWENTY
				+ pScad.substring(5, 7);
	}
 
	final String bufferDateStringXP(String pScad) {
		StringBuffer buffer = new StringBuffer();
 
		String month = pScad.substring(3, 5);
 
		buffer.append(pScad.substring(1, 3));
		buffer.append(POINT);
 
		buffer.append(MONTHS.valueOf(month).month);
		buffer.append(POINT);
 
		buffer.append(TWENTY);
		buffer.append(pScad.substring(5, 7));
		return buffer.toString();
	}
 
}

 

The results in details:

blogJoinString2

JMH how to tips

JMH is a good tool for benchmarking your code.

The following hints will help you to save time in an otherwise not easy set up.

  • always use state and scope at class level. Use Scope.Thread when uncertainn. Like:
    @State(Scope.Thread)
    public class MyClass {
  • No benchmarks to run message? check the include/exclude regexps.
  • Always use Fork s greater than zero.
  • Methods annotated with @Benchmark should be public.
  • Always return something from a benchmarked method.
  • Do you need to organize many becnhmarks though many classes? Use the command line to select a specific benchmark to run. Like:

Please refer to this  document for more hints.