Commit 4bb69119 authored by Ivo Anjo's avatar Ivo Anjo

Add new API with doTransactionally that relies on warming up transactions before use

Warming up transactions allows the JVM to JIT and do some initializations before code is run inside
a rtm transaction. Using this technique, we can call multiple methods inside a transaction and
still successfully commit on haswell's smallish rtm buffer.

API still work in progress.
parent 6dbef170
......@@ -78,7 +78,9 @@ JNIEXPORT jboolean JNICALL Java_javartm_Transaction_inTransaction(JNIEnv *env, j
// Used as a maximum for the pause instructions; see comments on Java_javartm_Transaction_begin
#define PAUSETIMES_LIMIT 64
JNIEXPORT jint JNICALL Java_javartm_Transaction_begin(JNIEnv *env, jclass cls) {
// Begin is used in multiple methods, but we force it to be inlined to avoid extra work after
// the transaction is started
__attribute__((always_inline)) inline int begin() {
int status;
int failtimes = 0;
while ((status = _xbegin()) == (_XABORT_RETRY | _XABORT_CONFLICT)) {
......@@ -100,6 +102,10 @@ JNIEXPORT jint JNICALL Java_javartm_Transaction_begin(JNIEnv *env, jclass cls) {
return status;
}
JNIEXPORT jint JNICALL Java_javartm_Transaction_begin(JNIEnv *env, jclass cls) {
return begin();
}
JNIEXPORT void JNICALL Java_javartm_Transaction_commit(JNIEnv *env, jclass cls) {
if (_xtest()) {
_xend();
......@@ -399,6 +405,7 @@ JNIEXPORT void JNICALL Java_javartm_Transaction_abort__J(JNIEnv *env, jclass cls
}
}
/*
JNIEXPORT jobject JNICALL Java_javartm_Transaction_doTransactionally(JNIEnv *env, jclass cls, jobject atomicBlock, jobject fallbackBlock) {
static jmethodID callMethodId = NULL;
if (callMethodId == NULL) {
......@@ -419,3 +426,18 @@ JNIEXPORT jobject JNICALL Java_javartm_Transaction_doTransactionally(JNIEnv *env
printf("Abort or failed to start tx res = %d\n", res);
return (*env)->CallObjectMethod(env, fallbackBlock, callMethodId);
}
*/
JNIEXPORT void JNICALL Java_javartm_Transaction_doTransactionally(JNIEnv *env, jclass cls, jobject runnable, jboolean warmup) {
static jmethodID runMethodId = NULL;
if (runMethodId == NULL) {
jclass atomicBlockClass = (*env)->FindClass(env, "java/lang/Runnable");
runMethodId = (*env)->GetMethodID(env, atomicBlockClass, "run", "()V");
if (!runMethodId) return;
}
if (warmup || (begin() == _XBEGIN_STARTED)) {
(*env)->CallVoidMethod(env, runnable, runMethodId);
if (!warmup) _xend();
}
}
......@@ -24,29 +24,31 @@ public final class Test2 {
public static int x, y, z;
public static void simpleTx() {
// dummy();
// dummy();
// dummy();
if (Transaction.begin() == Transaction.STARTED) {
x = 2;
y = 2;
z = 2;
// dummy();
dummy();
Transaction.commit();
}
}
// public static void dummy() { }
public static void dummy() { }
public static void main(String[] args) {
Warmup.doWarmup(new Runnable() { public void run() {
dummy();
}});
long iters = 0;
while (x == 0) {
iters++;
simpleTx();
// if (iters == 10) return;
if (iters % 100000000 == 0) System.out.println("Trying... " + iters);
if (iters == 1000000) {
System.out.println("Given up after " + iters + " iterations");
return;
}
}
//System.out.println("Transaction succeeded " + iters + " iters (" + x + ", " + y + ", " + z + ")");
System.out.println("Transaction succeeded (x: " + x + ", y: " + y + ", z: " + z + ")");
System.out.println("Transaction succeeded after " + iters + " iters (" + x + ", " + y + ", " + z + ")");
}
}
......@@ -19,30 +19,35 @@
*/
package javartm;
import java.util.concurrent.Callable;
public final class Test3 {
public static int x, y;
public static void main(String[] args) {
Callable<Boolean> simpletx = new Callable<Boolean>() {
public Boolean call() {
Test3.x = 1;
Test3.y = 1;
return true;
public static void main(String[] args) throws Throwable {
final Runnable transaction = new Runnable() {
public void run() {
if (!Transaction.inTransaction()) return;
x++;
y++;
}
};
Callable<Boolean> fallback = new Callable<Boolean>() {
public Boolean call() { return false; }
};
Warmup.doWarmup(transaction);
x = 0;
y = 0;
System.out.println("Starting test...");
long iters = 0;
while (x == 0) {
iters++;
Transaction.doTransactionally(simpletx, fallback);
if (iters % 100 == 0) System.out.println("Trying... " + iters);
Transaction.doTransactionally(transaction);
if (iters == 1000000) {
System.out.println("Given up after " + iters + " iterations");
return;
}
}
System.out.println("Transaction succeeded after " + iters + " iters (" + x + ", " + y + ")");
}
}*/
}
/*
* javartm: a Java library for Restricted Transactional Memory
* Copyright (C) 2013 Ivo Anjo <ivo.anjo@ist.utl.pt>
*
* This file is part of javartm.
*
* javartm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* javartm 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with javartm. If not, see <http://www.gnu.org/licenses/>.
*/
package javartm;
public final class Test6 {
public static long x, y, z, k;
public static void dummy() { k++; }
public static void main(String[] args) throws Throwable {
final int ITERS = 5000000;
final Runnable transaction = new Runnable() {
public void run() {
// The simpler warmup just returns with this test, but then the dummy method
// call will never work
//if (!Transaction.inTransaction()) return;
x++;
y++;
z++;
dummy();
}
};
Warmup.doWarmup(transaction);
x = 0; y = 0; z = 0;
Thread[] threads = new Thread[Runtime.getRuntime().availableProcessors()];
synchronized (Test6.class) {
for (int i = 0; i < threads.length; i++) {
System.out.println("Creating thread " + i);
final int myId = i;
threads[i] = new Thread() {
@Override public void run() {
// Poor man's barrier
synchronized (Test6.class) { new Object(); }
for (int i = 0; i < ITERS; i++) {
Transaction.doTransactionally(transaction);
}
System.out.println("Thread " + myId + " done " + ITERS);
}
};
threads[i].start();
}
}
for (Thread t : threads) t.join();
if (x != y || y != z) throw new Error("Final values differ");
int total = ITERS * threads.length;
System.out.println("Final stats: " + x + " successful iters, " + total + " tried (" +
(x/((double) total) * 100) + "% commit rate)");
}
}
......@@ -40,10 +40,6 @@ final class TestRtmSupport {
public final class Transaction {
private static final Logger Log = LoggerFactory.getLogger(Transaction.class);
// Default JIT compiler threshold for current hotspot versions
// See also http://tinyurl.com/lgeabt3
private static final int HOTSPOT_JIT_THRESHOLD = 10000;
public static final int STARTED = -1;
public static final int ABORT_EXPLICIT = 1 << 0;
......@@ -99,9 +95,13 @@ public final class Transaction {
// linking during a transaction. This can be seen by using -verbose:jni to enable
// logging of dynamic linking operations.
// 2) By default, hotspot JIT-recompiles code at around 10k iterations, and trying
// to JIT during a transaction can cause it to keep aborting.
// to JIT during a transaction can cause it to keep aborting. The flag
// -XX:+PrintCompilation may be used to verify which methods are being recompiled.
Log.trace("Warming up methods");
for (int i = 0; i < HOTSPOT_JIT_THRESHOLD * 1.1; i++) {
final Runnable dummyRunnable = new Runnable() { public void run() { } };
Warmup.doWarmup(new Runnable() { public void run() {
inTransaction();
begin();
// the abort on the next line makes sure no transaction stays active after the begin
......@@ -112,7 +112,14 @@ public final class Transaction {
catch (IllegalStateException expected) { }
try { commit(); throw new Error("Should never happen"); }
catch (IllegalStateException expected) { }
}
doTransactionally(dummyRunnable, true);
}});
Warmup.doWarmup(new Runnable() { public void run() {
doTransactionally(dummyRunnable);
}});
Log.info("initialization complete");
}
private Transaction() { }
......@@ -130,6 +137,12 @@ public final class Transaction {
//public native static <V> V doTransactionally(Callable<V> atomicBlock, Callable<V> fallbackBlock);
public static void doTransactionally(Runnable r) {
doTransactionally(r, false);
}
public native static void doTransactionally(Runnable r, boolean warmup);
public static short getAbortReason(int txStatus) {
return (short) (txStatus >>> 24);
}
......
/*
* javartm: a Java library for Restricted Transactional Memory
* Copyright (C) 2013 Ivo Anjo <ivo.anjo@ist.utl.pt>
*
* This file is part of javartm.
*
* javartm is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* javartm 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with javartm. If not, see <http://www.gnu.org/licenses/>.
*/
package javartm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Warmup {
private static final Logger Log = LoggerFactory.getLogger(Warmup.class);
// Default JIT compiler threshold for current hotspot versions
// See also http://tinyurl.com/lgeabt3
private static final int HOTSPOT_JIT_THRESHOLD = 10000;
// In some cases, the value above is not enough, so let's arbitrarily do a bit more
private static final int ITERATIONS = HOTSPOT_JIT_THRESHOLD * 3;
/** Warmups up the received runnable by calling it repeatedly until the VM JIT kicks in **/
public static void doWarmup(Runnable r) {
for (int i = 0; i < ITERATIONS; i++) r.run();
try {
Thread.sleep(10);
} catch (InterruptedException e) { throw new RuntimeException(e); }
Log.info("Warmup for " + r.getClass().getName() + " complete");
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment