Archiv der Kategorie: development

MSETEX – command in Redis

Redis has multiple commands that work on a number of keys. For example, you can use „MSET“ to set a number of keys to individual values. And you can use „SET“ or „SETEX“ to set a single key to a value with expire time. However, there is no command that handles multiple keys with expire time.

Since you can run lua-scripts in Redis, it is possible to build a custom MSETEX-command with lua. Here is the lua-code:

for i=1, #KEYS, 1 do 
    redis.call('SET', KEYS[i], ARGV[(i*2)-1] , 'EX', ARGV[(i*2)]) 
end 
return 'OK'

Remarks:

  • in contrast to some programming languages, lua uses 1 as the first index of a collection
  • „return ‚OK'“ is needed for vert.x, as vert.x-redis requires a non-null result

I’m not a lua expert, so feel free to improve the script if you spot something.

OK, the script is there, how do I use it? I use vert.x, so the first thing to do is set up a redis connection. Use „scriptLoad“ to send the script to redis, and store the result-hash. Now you can use the „evalsha“ command to run the script with your values. Here is some java-code for vert.x:

        List<String> keys = new ArrayList<>(entries.size());
        List<String> values = new ArrayList<>(entries.size()*2);
        for (int i = 0; i < 2; i++) {
            keys.add(i, "key"+i);
            values.add((i*2),"value");
            values.add((i*2)+1,"60"); //TTL in seconds
        }
        redisConnection.evalsha(msetexHash,keys,values,handler);

Remarks:

  • Collections are 0-based in java, thus the index magic is differs from lua
  • For each key you must specify 2 values: first the payload to set, then the ttl
  • error handling not implemented here
  • msetexHash must be initialized with the result of the „script load“ command
  • while the script is evaluated, redis blocks. Run some tests before using a large number of keys

Der Performance auf der Spur – Teil 2 (Projektbeschreibung)

Herzstück des Beispielprojekts ist eine Klasse namens „Calculator“. Diese führt Berechnungen auf Interfaces durch. Das Interface hat entweder eine Implementierung (für den monomorphen Fall), zwei (bimorph) oder 3 (trimorph oder megamorph).

Der Code von Calculator:

package net.modellierung;

public class Calculator {

    public long add(Monomorph m1, Monomorph m2) {
        return m1.getValue() + m2.getValue();
    }

    public long add(Bimorph b1, Bimorph b2) {
        return b1.getValue() + b2.getValue();
    }

    public long add(Trimorph t1, Trimorph t2) {
        return t1.getValue() + t2.getValue();
    }
}

Die Klassen des Projekts – Interfaces mit einer bestimmten Anzahl von Implementierungen:

Klassendiagramm Performanceprojekt

Die Implementierungen der Interfaces besitzen einen Konstruktor mit einem Long-Parameter. Dieser Wert wird beim Aufruf von getValue() zurückgegeben.

Erwartetes Verhalten des JIT-Compilers ist, dass er im ersten Fall Code generieren kann, der auf die eine Implementierung zugeschnitten ist, und damit den teuren virtuellen Methodenaufruf umgeht. Trotzdem müssen in diesem Fall Instruktionen zur Absicherung des Typs generiert werden, da zur Laufzeit eine weitere Implementierung nachgeladen werden kann. Der kompilierte Code muss das merken und dann eine Deoptimierung durchführen (also wieder den Bytecode ausführen und ggf. neuen Code (der beide Implementierungen berücksichtigt) generieren.
Im zweiten Fall weiß der JIT-Compiler, dass es zwei Implementierungen gibt. Der generierte Code kann aber mit einem Typcheck beide Implementierungen unterscheiden und somit auch den virtuellen Aufruf sparen. Auch hier muss der generierte Code Logik enthalten, um ggf. nachgeladene Implementierungen zu berücksichtigen.
Der dritte Fall soll hingegen nicht optimierbar sein, es müssen also virtuelle Aufrufe stattfinden.

Richard Warburton hat bereits mit einer etwas anderen Fragestellung einen interessanten Artikel geschrieben.

Das Java Memory Model

Als Java-Programmierer macht man sich im Allgemeinen keine Gedanken über die Hardware, auf der das Programm ausgeführt wird. Das ist eine riesige Erleichterung für Entwickler, aber das bedeutet auch, dass sich JVM und Compiler um das gleiche Verhalten auf unterschiedlicher Hardware (Multiprozessorsysteme) kümmern müssen. Mit Java 5 wurde der JSR-133 umgesetzt, dieser beschreibt das Java Memory Model.

Ein Programm mit mehreren Threads kann bei mehrmaliger Ausführung jeweils einen anderen Ablauf haben (je nachdem, wann ein anderer Thread aktiviert wird). Das Memory Model definiert also nicht einen bestimmten Ablauf für ein Programm, sondern bietet die Möglichkeit, eine bestimmten Ausführungsreihenfolge für ein Programm zu verifizieren. Der Entwickler kann sich einen Ablauf überlegen, in dem das Programm ein falsches Verhalten zeigt, und das Memory Model sagt dann, ob dieser für das Programm gültig ist. Da sowohl der Compiler, als auch der Prozessor pro Thread unabhängige Statements in eine andere Reihenfolge bringen können, gibt es sehr viele potentielle Fehler. Das Memory Model bietet aber über Synchronisierung die Möglichkeit, eine threadübergreifende Vorher-Nachher-Beziehung zwischen Programmteilen zu definieren.

Das Memory Model ist in der „Java Language and Virtual Machine Specification“  beschreiben, die für Java 8 im Abschnitt 17.4. Die Spezifikation ist relativ einfach zu Lesen mit vielen Beispielen, ich empfehle die Lektüre!

Weitere Hintergrundinformationen und Beispiele gibt es in dem Google Talk und auf der Seite http://www.cs.umd.edu/~pugh/java/memoryModel/.

ProGuard optimizer and obfuscator in multi-module maven projects

ProGuard is a tool that processes java .class files and generates smaller, faster and harder to read bytecode.
I had to use it for a single module in a multi-module maven project, and found no documentation for that, thus I describe the steps here.

In the module that should be obfuscated, a build step has to be configured in maven. The important thing here is the configuration with „<attach>true“, so that the obfuscated file is exported to the repository and hence can be used by other modules.

    <build>
        <plugins>
            <plugin>
                <groupId>com.github.wvengen</groupId>
                <artifactId>proguard-maven-plugin</artifactId>
                <version>2.0.8</version>
                <dependencies>
                    <dependency>
                        <groupId>net.sf.proguard</groupId>
                        <artifactId>proguard-base</artifactId>
                        <version>5.2</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>proguard</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <proguardVersion>5.2</proguardVersion>
                    <attach>true</attach>
                    <obfuscate>true</obfuscate>
                    <options>
                        <option>-keep public class package.ParameterObject{ public *;}</option>
                        <option>-keep public class package.Implementation{public static void
                            process(package.ParameterObject);}
                        </option>
                        <option>-optimizationpasses 4</option>
                    </options>
                    <libs>
                        <lib>${java.home}/lib/rt.jar</lib>
                    </libs>
                </configuration>
            </plugin>
        </plugins>
    </build>

If you run the build, you can see that two artifacts are generated now by this module: A module-1.0.0-SNAPSHOT.jar that has the same content as before, and a module-1.0.0-SNAPSHOT-small.jar that contains the small and obfuscated code. You can pick another name instead of „small“ by specifying the „attachArtifactClassifier“-property in the configuration.

How do you use this obfuscated code from another module in the project? Pretty easy, the small-suffix (or however you named it) can be used as the classifier in the maven-dependency:

        <dependency>
            <groupId>...</groupId>
            <artifactId>...</artifactId>
            <classifier>small</classifier>
        </dependency>

Run the build and look at the packaged artifact (for example a .war), it will no longer contain the clear bytecode, but only the obfuscated library.