Benchmarking mit dem JMH-Framework

Benchmarking unter Java hat mit einigen Problemen zu kämpfen. So optimiert die Java-Laufzeitumgebung den Quellcode, je nachdem wie oft er benutzt wird. Wenn nun kleinere Dinge getestet werden sollen, wie z.B. ob Methode A oder B besser funktioniert so wird dies problematisch. Am Anfang würde besagte Methoden nur interpretiert werden, anschließend würden sie bei häufiger Benutzung kompiliert werden und bei noch häufigerer Benutzung weiter optimiert werden. Daneben kann es passieren dass der Benchmark komplett wegoptimiert wird, da die JVM unter Umständen erkennt, dass die genutzten Werte bzw. die Ergebnisse des Benchmarks später nicht mehr genutzt werden.

Die offizielle Seite des Java Microbenchmark Harness-Framework

Damit sich ein Entwickler nicht immer und immer wieder mit solchen Problemen beim Benchmarking herumschlagen muss, wird seit einigen Jahren im Rahmen des OpenJDK-Projektes an dem Java Microbenchmark Harness-Framework gearbeitet. Das JMH-Framework nimmt dem Entwickler viele Aufgaben ab, um diese Probleme zu lösen. So kann bzw. wird die JVM durch JMH aufgewärmt werden und auch die Messung der Zeiten übernimmt JMH. Um JMH zu nutzen, müssen die entsprechenden Abhängigkeiten (in diesem Fall über die Maven-POM-Datei) dem Projekt hinzugefügt werden:

<dependency>
	<groupId>org.openjdk.jmh</groupId>
	<artifactId>jmh-core</artifactId>
	<version>1.21</version>
</dependency>
<dependency>
	<groupId>org.openjdk.jmh</groupId>
	<artifactId>jmh-generator-annprocess</artifactId>
	<version>1.21</version>
</dependency>

Sind die Abhängigkeiten eingebunden, kann mit dem eigentlichen Aufbau des Benchmarks begonnen werden. Jede Methode, welche ein Benchmark durchführen möchte, muss mit der @Benchmark-Annotation gekennzeichnet werden:

@Benchmark
public int addInts() {
    return aInt + bInt;
}

Die Klasse, in welcher die Benchmarks definiert sind, muss mit einigen weiteren Annotationen ausgestattet werden:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
public class AddingBenchmark {
...

Die @State(Scope.Benchmark)-Annotation in diesem Beispiel sorgt dafür das die Felder im Rahmen des Benchmarks für die Benchmark-Methoden zur Verfügung stehen. Mit der @BenchmarkMode-Annotation wird definiert, welche Art von Messung vorgenommen werden soll. In diesem Fall wird die durchschnittliche Zeit für eine einzelne Operation gemessen und ausgegeben. Über die @OutputTimeUnit-Annotation wird definiert wie die Zeiten für die Messungen ausgegeben werden. Werden für den Benchmark Daten benötigt so können sie über eine Methode, welche mit der @SetupAnnotation versehen wird, erzeugt werden:

@Setup
public void setup() {
    ...
}

Die setup-Methode wird damit vor dem eigentlichen Benchmark ausgeführt und somit können benötigte Daten dort erzeugt werden. Damit der Benchmark nun ausgeführt werden kann muss das Java Microbenchmark Harness-Framework angestartet werden. Dafür wird eine Klasse geschrieben und mit einer main-Methode versehen:

package net.seeseekey.JavaBenchmark;

import org.openjdk.jmh.runner.RunnerException;

import java.io.IOException;

public class Runner {

    public static void main(String[] args) throws IOException, RunnerException {
        
        org.openjdk.jmh.Main.main(args);
    }
}

Über die main-Methode wird die main-Methode des Frameworks gestartet und damit wird das Benchmark durchgeführt. Alternativ kann die Methode org.openjdk.jmh.Main.main auch direkt in der Maven-POM-Datei als Startklasse definiert werden:

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.0</version>
    <configuration>
        <archive>
            <manifest>
                <mainClass>org.openjdk.jmh.Main.main</mainClass>
            </manifest>

Nachdem der Benchmark implementiert ist, kann dieser natürlich über die IDE ausgeführt werden. Das würde allerdings nicht ganz der Intention von JMH entsprechen. Stattdessen sollte eine JAR aus dem Projekt erzeugt werden und diese, auf einem möglichst unbelastetem System, über die Konsole ausgeführt werden:

java -jar benchmark.jar

Damit wird der Benchmark über JMH ausgeführt. JMH beginnt mit einer Warmup-Phase und führt anschließend den eigentlichen Benchmark durch. Nach einigen Minuten erhält der Entwickler die Auswertung des Benchmark:

Benchmark                                 Mode  Cnt   Score    Error  Units
JavaBenchmark.AddingBenchmark.addDoubles  avgt   25  ≈ 10⁻⁵           ms/op
JavaBenchmark.AddingBenchmark.addInts     avgt   25  ≈ 10⁻⁵           ms/op

In diesem Fall sieht der Entwickler bei der Ausgabe, dass die Ausgabeeinheit für die Zeit mit Millisekunden zu grob gewählt wurde und stattdessen besser Nanosekunden genutzt werden sollten.

Tastatur und Maus unter Java fernsteuern

Für bestimmte Automatisierungsaufgaben ist es manchmal nötig die Tastatur und die Maus eines Rechners fernzusteuern. Unter Java kann dies mit der Klasse Robot aus dem Package java.awt erledigt werden. Ein einfaches Beispiel zur Nutzung sieht dabei wie folgt aus:

// Create robot
Robot robot = new Robot();

// Move mouse and make a mouse click, then wait
robot.mouseMove(1000, 1000);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
robot.delay(1000);

// Press key a, then wait
robot.keyPress(KeyEvent.VK_A);
robot.keyRelease(KeyEvent.VK_A);
robot.delay(1000);

In diesem Beispiel wird zunächst eine Instanz der Klasse Robot angelegt. Anschließend wird die Maus bewegt und die linke Maustaste gedrückt und wieder losgelassen. Danach wartet der Robot eine Sekunde, um anschließend die Taste A zu drücken. Neben den im Beispiel gezeigten Funktionalität kann unter anderem das Mausrad gesteuert werden. Auch Methoden um einen Pixel auszulesen oder größere Bereiche des Bildschirmes sind in der Klasse zu finden.

Probleme mit NativeScript und Xcode

NativeScript ist ein Framework für die Entwicklung von plattformübergreifenden Apps. Um aus dem NativeScript-Quellcode eine lauffähige iOS-App zu generieren, muss unter anderem Xcode installiert sein. Dabei kann es im Zusammenhang mit NativeScript und xcodebuild zu folgender Fehlermeldung kommen:

xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance

Gelöst werden kann dieses Problem auf dem Terminal mit dem Befehl:

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer

Anschließend sollte bei der Ausführung des Befehls:

xcodebuild -version

folgende Ausgabe zu sehen sein:

Xcode 10.2
Build version 10E125

Damit ist das Problem gelöst und mit der Entwicklung mittels NativeScript kann fortgefahren werden.

Freie Grammatik- und Rechtschreibprüfung

Vor einigen Jahren schrieb ich einen kurzen Artikel über die Nutzung von LanguageTool in der freien Office-Lösung LibreOffice. LanguageTool ist eine freie Software zur Rechtschreib- und Grammatikprüfung, ähnlich dem wahrscheinlich im deutschen Raum bekannteren Duden Korrektor. Allerdings ist LanguageTool weit mehr als eine Erweiterung für LibreOffice. Es handelt sich um eine in Java entwickelte Software, welche mittlerweile über 30 Sprachen unterstützt. Mittlerweile gibt es neben der freien Version, einen Dienst, welcher weitere Korrekturen gegen Entgelt bereitstellt (languagetoolplus.com).

LanguageTool in der Standalone-Version

Neben der Standalone-Desktop-Version existieren unzählige Integrationen, wie z.B. für LibreOffice, Firefox und Chrome in Form von Add-ons. Neben den offiziellen Add-ons existieren weitere Add-ons, welche durch die Community bereitgestellt werden. Die offizielle Seite des Projektes ist unter languagetool.org zu finden. Die Entwicklung findet auf GitHub statt. Dort ist ebenfalls der Quelltext von LanguageTool und etwaiger Erweiterungen zu finden. Lizenziert ist LanguageTool unter der LGPL.

Wiki für prozedural generierte Inhalte

In einem gewöhnlichen Spiel, falls sowas überhaupt existiert, ist der entsprechende Inhalt im Spiel hinterlegt. Allerdings existieren auch Spiele welche den Inhalt erst beim Spielen des Spieles generieren. In den meisten Fällen kommen prozedurale Verfahren zum Einsatz. Beispiele für solche Spiele sind Elite, No Man’s Sky und auch Minecraft.

Die Procedural Content Generation Wiki

Wer als Entwickler solche Inhalte in ein Spiel integrieren möchte, der muss nicht bei Null anfangen, sondern kann sich im Procedural Content Generation Wiki mit entsprechenden Informationen versorgen. Dort finden sich Informationen zu Spielen, Algorithmen und vielem mehr rund um die prozedurale Generierung von Inhalten. Zu finden ist die Wiki unter pcg.wikidot.com.