package org.corfudb.samples;
import org.corfudb.runtime.collections.SMRMap;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Sometimes developers may group mutator operations into transactions for performance reasons.
*
* This program runs two such workloads.
*
* Workload 1:
* This workload populates a large map with entries, in two ways,
* - default, where one mutation is dumped to the log
* - in batches, each batch is a "transaction", dumped to the log as an indivisuble entry.
*
* Running on my macbook, the first loop takes (roughly) 57 seconds, the second one 5 seconds.
*
* The reason is that internally, the Corfu corfuRuntime sends updates to the Corfu log to persist.
* When an application performs a bunch of individual updates, they are persisted one at a time,
* each in a separate log entry.
*
* In a transaction, the corfuRuntime waits until the transaction end and persists one (large) log entry
* for the entire transaction. Writing one large entry takes much less time than many small ones.
*
* Workload 2:
*
* This workload also populates map entries in transaction batches. However, it works over a collection of maps.
* Transactions perform updates to distinct streams.
*
* Running again on a single mac-book, it takes (roughly) 59 seconds.
* The reason is that, despite the transaction batching, each stream needs to persist the transaction independently.
* So the load is the same as performing individual updates, one by one.
*
* ======
*
* Note: For either workload, this sort of batching must be done with care,
* to avoid causing false-sharing and inflicting aborts.
*
* Created by dalia on 12/30/16.
*/
public class BatchedWriteTransaction extends BaseCorfuAppUtils {
/**
* main() and standard setup methods are deferred to BaseCorfuAppUtils
* @return
*/
static BaseCorfuAppUtils selfFactory() { return new BatchedWriteTransaction(); }
public static void main(String[] args) { selfFactory().start(args); }
private final int numBatches = 100;
private final int batchSize = 1_000;
private final int numTasks = numBatches * batchSize;
/**
* this method initiates activity
*/
@Override
void action() {
workload1();
workload2();
}
void workload1() {
/**
* Instantiate a Corfu Stream named "A" dedicated to an SMRmap object
*/
Map<String, Integer> map = getCorfuRuntime().getObjectsView()
.build()
.setStreamName("A") // stream name
.setType(SMRMap.class) // object class backed by this stream
.open(); // instantiate the object!
// populate map: sequentially
long startt = System.currentTimeMillis();
for (int i = 0; i < numTasks; i++) {
map.put("k1" + i, i);
}
long endt = System.currentTimeMillis();
System.out.println("time to populate map sequentially (msecs): " + (endt - startt) );
// populate map: in batched transactions
startt = System.currentTimeMillis();
for (int i = 0; i < numBatches; i++) {
getCorfuRuntime().getObjectsView().TXBegin();
for (int j = 0; j < batchSize; j++)
map.put("k2" + (i * batchSize + j), i);
getCorfuRuntime().getObjectsView().TXEnd();
}
endt = System.currentTimeMillis();
System.out.println("time to populate map in batches (msecs): " + (endt - startt));
}
void workload2() {
/**
* Instantiate a collection of Corfu streams, each one dedicated to an SMRmap instance
*/
List<Map<String, Integer>> maparray = new ArrayList<Map<String, Integer>>(numBatches);
for (int m = 0; m < numBatches; m++) {
maparray.add(m,
getCorfuRuntime().getObjectsView()
.build()
.setStreamName("C" + m)
.setType(SMRMap.class)
.open()
);
}
long startt = System.currentTimeMillis();
for (int i = 0; i < batchSize; i++) {
getCorfuRuntime().getObjectsView().TXBegin();
for (int j = 0; j < numBatches; j++)
maparray.get(j).put("k2"+(i*numBatches + j), i);
getCorfuRuntime().getObjectsView().TXEnd();
}
long endt = System.currentTimeMillis();
System.out.println("time to populate map-array in batches (msecs): " + (endt-startt));
}
}