HZDistributedAtomicLong measurement code

- Part of ONOS-1767

Change-Id: I635ddde1863dffb8d68fa83e22bb7dde7c44136e
diff --git a/conf/hazelcast.default.xml b/conf/hazelcast.default.xml
index a05ff8c..9704736 100644
--- a/conf/hazelcast.default.xml
+++ b/conf/hazelcast.default.xml
@@ -14,6 +14,11 @@
 
   <network>
     <port auto-increment="true">5701</port>
+<!-- Examples to force using certain interface
+    <interfaces enabled="true">
+      <interface>192.168.200.*</interface>
+    </interfaces>
+-->
     <join>
 <multicast enabled="false" />
 <tcp-ip enabled="true">
diff --git a/conf/template/hazelcast.xml.template b/conf/template/hazelcast.xml.template
index d06addc..8ca2e3f 100644
--- a/conf/template/hazelcast.xml.template
+++ b/conf/template/hazelcast.xml.template
@@ -20,6 +20,11 @@
 
   <network>
     <port auto-increment="true">__HC_PORT__</port>
+<!-- Examples to force using certain interface
+    <interfaces enabled="true">
+      <interface>192.168.200.*</interface>
+    </interfaces>
+-->
     <join>
 __HC_NETWORK__
       <aws enabled="false">
diff --git a/pom.xml b/pom.xml
index 9ccd5d9..a0bca46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -761,6 +761,12 @@
       <version>${powermock.version}</version>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math3</artifactId>
+      <version>3.3</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
   <profiles>
     <!-- Jenkins by default defines a property BUILD_NUMBER which is used to 
diff --git a/src/test/java/net/onrc/onos/core/datastore/hazelcast/HZDistributedAtomicLongTest.java b/src/test/java/net/onrc/onos/core/datastore/hazelcast/HZDistributedAtomicLongTest.java
index 4813672..160e015 100644
--- a/src/test/java/net/onrc/onos/core/datastore/hazelcast/HZDistributedAtomicLongTest.java
+++ b/src/test/java/net/onrc/onos/core/datastore/hazelcast/HZDistributedAtomicLongTest.java
@@ -18,17 +18,22 @@
 import net.onrc.onos.core.datastore.ObjectDoesntExistException;
 import net.onrc.onos.core.util.distributed.DistributedAtomicLong;
 
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
+import com.hazelcast.core.HazelcastInstance;
+
 /**
  * Test cases for HZDistributedAtomicLong.
  */
 public class HZDistributedAtomicLongTest {
 
     static final String TEST_COUNTER_NAME = "Counter" + UUID.randomUUID();
+    private static final int SEC_IN_NANO = 1000000000;
 
-    DistributedAtomicLong counter;
+    private DistributedAtomicLong counter;
 
     @Before
     public void setUp() throws Exception {
@@ -120,6 +125,12 @@
                                         System.getProperty("NUM_INCREMENTS",
                                                            "100")));
 
+    private static final int ROUNDS = Integer.parseInt(
+                            System.getProperty(
+                                    "HZDistributedAtomicLongTest.ROUNDS",
+                                      System.getProperty("ROUNDS",
+                                                         "100")));
+
     /**
      * Increment using multiple threads to test addition is atomic.
      */
@@ -174,4 +185,72 @@
         executor.shutdown();
     }
 
+
+    /**
+     * incrementAndGet throughput measurement.
+     */
+    @Test
+    public void incrementThroughput() throws InterruptedException {
+        // This test will run only if -Dbenchmark is set to something
+        // e.g., mvn test -Dtest=HZDistributedAtomicLongTest#incrementThroughput
+        //                -Dbenchmark -DNUM_INCREMENTS=1000 -DROUNDS=10
+        Assume.assumeNotNull(System.getProperty("benchmark"));
+
+        // Warmup
+        counter.set(0);
+        for (int i = 0; i < NUM_INCREMENTS; i++) {
+            counter.incrementAndGet();
+        }
+        counter.set(0);
+        try {
+            Thread.sleep(5000);
+        } catch (InterruptedException e) { // CHECKSTYLE IGNORE THIS LINE
+        }
+
+        HazelcastInstance hz = HZClient.getClient().getHZInstance();
+        final int clusterSize = hz.getCluster().getMembers().size();
+
+        System.out.println("Starting benchmark with cluster size: " + clusterSize);
+        DescriptiveStatistics stats = new DescriptiveStatistics();
+
+        // Measurements
+        // Throughput calculated from total time it took to inc NUM_INCREMENTS
+        // Repeating ROUNDS time to get average Throughput, etc.
+        for (int i = 0; i < ROUNDS; i++) {
+            long timeBegin = System.nanoTime();
+            for (int j = 0; j < NUM_INCREMENTS; j++) {
+                counter.incrementAndGet();
+            }
+            long timeEnd = System.nanoTime();
+            double throughput = (double) NUM_INCREMENTS * SEC_IN_NANO
+                                        / (timeEnd - timeBegin);
+            stats.addValue(throughput);
+            System.out.println("Increments: " + NUM_INCREMENTS
+                            + " IncrementThroughput(ops/s): " + throughput);
+        }
+
+        System.out.println();
+
+        System.out.println("incrementAndGet Throughput (ops/s) "
+                        + "[ " + NUM_INCREMENTS + " increments] "
+                        + "[ " + clusterSize + " HZnodes]");
+
+        System.out.println(stats.toString());
+        //DescriptiveStatistics:
+        //    n: 100
+        //    min: 1137.5270162666363
+        //    max: 4056.1369351829317
+        //    mean: 2727.695488835985
+        //    std dev: 704.2206793204389
+        //    median: 2729.6338956455156
+        //    skewness: -0.17084469855647005
+        //    kurtosis: -0.6018103898245659
+
+        // Wait for other instances stops incrementing, before exiting
+        long prev = counter.get();
+        while (prev != counter.get()) {
+            prev = counter.get();
+            Thread.sleep(1000);
+        }
+    }
 }