I asked myself: What happend with Transactional Memory and the Intel TSX Extension? I found a lot about TSX-bugs in CPUs, but I did not find examples, tutorials, best practices.
But I found the "-server -XX:+UseRTMLocking" JVM-option. I wrote a little testprogram and I was surprised about the effect this option can have on execution time. (Intel i7 6700K, jvm 8.131)
Does anybody have experience with this flag?
Greetings, Thomas
Here the program:
import java.util.*;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TSXTest {
final int N;
final Object o = new Object();
final int[] variables;
public TSXTest(int n) {
this.N = n;
variables = new int[N];
}
public static void main(String[] args) throws InterruptedException {
int n = 100000;
while (true) {
new TSXTest(n).go(false);
if (n == 1) {
break;
}
n = n / 10;
}
n = 100000;
while (true) {
new TSXTest(n).go(true);
if (n == 1) {
break;
}
n = n / 10;
}
}
private void go(boolean doSync) throws InterruptedException {
// -XX:+UseRTMLocking
ExecutorService es = Executors.newFixedThreadPool(4);
Collection cs = new ArrayList<>();
cs.add(new Task(100, doSync));
cs.add(new Task(200, doSync));
cs.add(new Task(300, doSync));
cs.add(new Task(400, doSync));
long start = System.nanoTime();
es.invokeAll(cs);
es.shutdown();
System.out.println("synchronized: "+ doSync + "; Nmb of Variables:" + N + "; Duration:" + ((System.nanoTime() - start) / 1000000));
}
public class Task implements Callable<Void> {
final Random r;
private int seed;
private boolean sync;
private int[] rnd = new int[N];
public Task(int seed, boolean sync) {
r = new Random(seed);
this.seed = seed;
this.sync = sync;
List<Integer> rndList = new ArrayList<>();
for (int i = 0; i < N; i++) {
rndList.add(i);
}
Collections.shuffle(rndList, r);
for (int i = 0; i < N; i++) {
rnd[i] = rndList.get(i).intValue();
}
}
@Override
public Void call() throws Exception {
for (int i = 0; i < 100000000; i++) {
if (sync)
synchronized (o) {
variables[rnd[i % N]]++;
}
else variables[rnd[i % N]]++;
}
return null;
}
}
}
And the results:
with "-server -XX:+UseRTMLocking"
synchronized: false; Nmb of Variables:100000; Duration:1092
synchronized: false; Nmb of Variables:10000; Duration:1390
synchronized: false; Nmb of Variables:1000; Duration:1561
synchronized: false; Nmb of Variables:100; Duration:2466
synchronized: false; Nmb of Variables:10; Duration:2118
synchronized: false; Nmb of Variables:1; Duration:391
synchronized: true; Nmb of Variables:100000; Duration:3133
synchronized: true; Nmb of Variables:10000; Duration:3400
synchronized: true; Nmb of Variables:1000; Duration:5406
synchronized: true; Nmb of Variables:100; Duration:15246
synchronized: true; Nmb of Variables:10; Duration:24955
synchronized: true; Nmb of Variables:1; Duration:23940
with "-server"
synchronized: false; Nmb of Variables:100000; Duration:1053
synchronized: false; Nmb of Variables:10000; Duration:1313
synchronized: false; Nmb of Variables:1000; Duration:1524
synchronized: false; Nmb of Variables:100; Duration:2242
synchronized: false; Nmb of Variables:10; Duration:2121
synchronized: false; Nmb of Variables:1; Duration:432
synchronized: true; Nmb of Variables:100000; Duration:39720
synchronized: true; Nmb of Variables:10000; Duration:23748
synchronized: true; Nmb of Variables:1000; Duration:21648
synchronized: true; Nmb of Variables:100; Duration:21243
synchronized: true; Nmb of Variables:10; Duration:17058
synchronized: true; Nmb of Variables:1; Duration:20845
What happens?
4 threads are modifying 100000000 times one of N variables.
The fastest version is, when there is no synchronisation at all.
When using TSX, the executiontime grows with growing conflict rate (big N --> small N ; short execution time --> long execution time)
With standard synchronisation, the execution time seams to grow with data size???
More complex operation: When using a slightly more complex operation, where synchronized cannot be replaced by the use of AtomicInteger, the result is similar.
...
@Override
public Void call() throws Exception {
for (int i = 0; i < 100000000; i++) {
if (sync)
synchronized (o) {
operation(i);
}
else operation(i);
}
return null;
}
private void operation(int i){
int i1 = variables[rnd[i % N]]++;
variables[rnd[(i+1) % N]] = i1 + 17;
}
}
}
with "-server -XX:+UseRTMLocking":
synchronized: false; Nmb of Variables:100000; Duration:1259
synchronized: false; Nmb of Variables:10000; Duration:876
synchronized: false; Nmb of Variables:1000; Duration:1351
synchronized: false; Nmb of Variables:100; Duration:2014
synchronized: false; Nmb of Variables:10; Duration:408
synchronized: false; Nmb of Variables:1; Duration:367
synchronized: true; Nmb of Variables:100000; Duration:4234
synchronized: true; Nmb of Variables:10000; Duration:3886
synchronized: true; Nmb of Variables:1000; Duration:7826
synchronized: true; Nmb of Variables:100; Duration:24587
synchronized: true; Nmb of Variables:10; Duration:23480
synchronized: true; Nmb of Variables:1; Duration:22382
with "-server":
synchronized: false; Nmb of Variables:100000; Duration:1264
synchronized: false; Nmb of Variables:10000; Duration:893
synchronized: false; Nmb of Variables:1000; Duration:1342
synchronized: false; Nmb of Variables:100; Duration:2036
synchronized: false; Nmb of Variables:10; Duration:402
synchronized: false; Nmb of Variables:1; Duration:333
synchronized: true; Nmb of Variables:100000; Duration:35944
synchronized: true; Nmb of Variables:10000; Duration:23031
synchronized: true; Nmb of Variables:1000; Duration:22476
synchronized: true; Nmb of Variables:100; Duration:24387
synchronized: true; Nmb of Variables:10; Duration:20457
synchronized: true; Nmb of Variables:1; Duration:22227