From 29ddcf4b75cdf9d834186ea5820aaaab94c2d37c Mon Sep 17 00:00:00 2001
From: Russell Sears <sears@yahoo-inc.com>
Date: Sat, 19 Nov 2011 16:49:29 -0800
Subject: [PATCH] Remove synchronization points from random number generation. 
 This mostly consisted switching from per-class instances of Random() that are
 shared amongst threads to thread-local instances shared across classes.

Also, CounterGenerator.nextInt() was synchronized.  Changed it to use an AtomicInteger.

Finally, removed lastString() from Generator, as it was only called in places where lastInt() was more appropriate; switched the calls to lastInt().
---
 src/com/yahoo/ycsb/BasicDB.java               |  5 +---
 src/com/yahoo/ycsb/Client.java                |  4 +---
 src/com/yahoo/ycsb/RandomByteIterator.java    |  5 +---
 src/com/yahoo/ycsb/Utils.java                 | 15 +++++++++---
 .../ycsb/generator/CounterGenerator.java      | 23 +++++++++++--------
 .../ycsb/generator/DiscreteGenerator.java     |  5 ++--
 .../ycsb/generator/ExponentialGenerator.java  |  8 +++----
 .../ycsb/generator/HistogramGenerator.java    |  5 ++--
 .../generator/HotspotIntegerGenerator.java    |  5 ++--
 .../ycsb/generator/IntegerGenerator.java      |  5 ++--
 .../ycsb/generator/SkewedLatestGenerator.java |  4 ++--
 .../generator/UniformIntegerGenerator.java    |  6 ++---
 .../ycsb/generator/ZipfianGenerator.java      |  8 +++----
 13 files changed, 51 insertions(+), 47 deletions(-)

diff --git a/src/com/yahoo/ycsb/BasicDB.java b/src/com/yahoo/ycsb/BasicDB.java
index 3b4adf63..9490451e 100644
--- a/src/com/yahoo/ycsb/BasicDB.java
+++ b/src/com/yahoo/ycsb/BasicDB.java
@@ -21,7 +21,6 @@ import java.util.HashMap;
 import java.util.Properties;
 import java.util.Set;
 import java.util.Enumeration;
-import java.util.Random;
 import java.util.Vector;
 
 
@@ -37,13 +36,11 @@ public class BasicDB extends DB
 	public static final String SIMULATE_DELAY_DEFAULT="0";
 	
 	
-	Random random;
 	boolean verbose;
 	int todelay;
 
 	public BasicDB()
 	{
-		random=new Random();
 		todelay=0;
 	}
 
@@ -54,7 +51,7 @@ public class BasicDB extends DB
 		{
 			try
 			{
-				Thread.sleep((long)random.nextInt(todelay));
+				Thread.sleep((long)Utils.random().nextInt(todelay));
 			}
 			catch (InterruptedException e)
 			{
diff --git a/src/com/yahoo/ycsb/Client.java b/src/com/yahoo/ycsb/Client.java
index 9da86041..64585f81 100644
--- a/src/com/yahoo/ycsb/Client.java
+++ b/src/com/yahoo/ycsb/Client.java
@@ -137,8 +137,6 @@ class StatusThread extends Thread
  */
 class ClientThread extends Thread
 {
-	static Random random=new Random();
-
 	DB _db;
 	boolean _dotransactions;
 	Workload _workload;
@@ -215,7 +213,7 @@ class ClientThread extends Thread
 		   //and the sleep() doesn't make sense for granularities < 1 ms anyway
 		   if ( (_target>0) && (_target<=1.0) ) 
 		   {
-		      sleep(random.nextInt((int)(1.0/_target)));
+		      sleep(Utils.random().nextInt((int)(1.0/_target)));
 		   }
 		}
 		catch (InterruptedException e)
diff --git a/src/com/yahoo/ycsb/RandomByteIterator.java b/src/com/yahoo/ycsb/RandomByteIterator.java
index 3e6cba18..99239f85 100644
--- a/src/com/yahoo/ycsb/RandomByteIterator.java
+++ b/src/com/yahoo/ycsb/RandomByteIterator.java
@@ -16,10 +16,7 @@
  */
 package com.yahoo.ycsb;
 
-import java.util.Random;
-
 public class RandomByteIterator extends ByteIterator {
-	static Random random=new Random();
 	long len;
 	long off;
 	int buf_off;
@@ -31,7 +28,7 @@ public class RandomByteIterator extends ByteIterator {
 	}
 
 	private void fillBytesImpl(byte[] buf, int base) {
-		int bytes = random.nextInt();
+		int bytes = Utils.random().nextInt();
 		try {
 			buf[base+0] = (byte)(((bytes      ) & 31) + ' ');
 			buf[base+1] = (byte)(((bytes >> 5 ) & 31) + ' ');
diff --git a/src/com/yahoo/ycsb/Utils.java b/src/com/yahoo/ycsb/Utils.java
index 57dd141a..f49bc0f5 100644
--- a/src/com/yahoo/ycsb/Utils.java
+++ b/src/com/yahoo/ycsb/Utils.java
@@ -24,8 +24,17 @@ import java.util.Random;
  */
 public class Utils
 {
-	static Random random=new Random();
-	
+  private static final Random rand = new Random();
+  private static final ThreadLocal<Random> rng = new ThreadLocal<Random>();
+
+  public static Random random() {
+    Random ret = rng.get();
+    if(ret == null) {
+      ret = new Random(rand.nextLong());
+      rng.set(ret);
+    }
+    return ret;
+  }
       /**
        * Generate a random ASCII string of a given length.
        */
@@ -34,7 +43,7 @@ public class Utils
 	 int interval='~'-' '+1;
 	
         byte []buf = new byte[length];
-        random.nextBytes(buf);
+        random().nextBytes(buf);
         for (int i = 0; i < length; i++) {
           if (buf[i] < 0) {
             buf[i] = (byte)((-buf[i] % interval) + ' ');
diff --git a/src/com/yahoo/ycsb/generator/CounterGenerator.java b/src/com/yahoo/ycsb/generator/CounterGenerator.java
index 11db1cf1..df37b239 100644
--- a/src/com/yahoo/ycsb/generator/CounterGenerator.java
+++ b/src/com/yahoo/ycsb/generator/CounterGenerator.java
@@ -17,34 +17,39 @@
 
 package com.yahoo.ycsb.generator;
 
+import java.util.concurrent.atomic.AtomicInteger;
+
 /**
  * Generates a sequence of integers 0, 1, ...
  */
 public class CounterGenerator extends IntegerGenerator
 {
-	int counter;
+	final AtomicInteger counter;
 
 	/**
 	 * Create a counter that starts at countstart
 	 */
 	public CounterGenerator(int countstart)
 	{
-		counter=countstart;
-		setLastInt(countstart-1);
+		counter=new AtomicInteger(countstart);
+		setLastInt(counter.get()-1);
 	}
 	
 	/**
 	 * If the generator returns numeric (integer) values, return the next value as an int. Default is to return -1, which
 	 * is appropriate for generators that do not return numeric values.
 	 */
-	public synchronized int nextInt() 
+	public int nextInt() 
 	{
-		int lastint=counter;
-		counter++;
-		setLastInt(lastint);
-		return lastint;
+		int ret = counter.getAndIncrement();
+		setLastInt(ret);
+		return ret;
+	}
+	@Override
+	public int lastInt()
+	{
+	                return counter.get() - 1;
 	}
-
 	@Override
 	public double mean() {
 		throw new UnsupportedOperationException("Can't compute mean of non-stationary distribution!");
diff --git a/src/com/yahoo/ycsb/generator/DiscreteGenerator.java b/src/com/yahoo/ycsb/generator/DiscreteGenerator.java
index 087312c0..c28f3ae7 100644
--- a/src/com/yahoo/ycsb/generator/DiscreteGenerator.java
+++ b/src/com/yahoo/ycsb/generator/DiscreteGenerator.java
@@ -20,6 +20,7 @@ package com.yahoo.ycsb.generator;
 import java.util.Vector;
 import java.util.Random;
 
+import com.yahoo.ycsb.Utils;
 import com.yahoo.ycsb.WorkloadException;
 
 /**
@@ -40,13 +41,11 @@ public class DiscreteGenerator extends Generator
 	}
 
 	Vector<Pair> _values;
-	Random _random;
 	String _lastvalue;
 
 	public DiscreteGenerator()
 	{
 		_values=new Vector<Pair>();
-		_random=new Random();
 		_lastvalue=null;
 	}
 
@@ -62,7 +61,7 @@ public class DiscreteGenerator extends Generator
 			sum+=p._weight;
 		}
 
-		double val=_random.nextDouble();
+		double val=Utils.random().nextDouble();
 
 		for (Pair p : _values)
 		{
diff --git a/src/com/yahoo/ycsb/generator/ExponentialGenerator.java b/src/com/yahoo/ycsb/generator/ExponentialGenerator.java
index 7a9187e1..de1a2760 100644
--- a/src/com/yahoo/ycsb/generator/ExponentialGenerator.java
+++ b/src/com/yahoo/ycsb/generator/ExponentialGenerator.java
@@ -19,6 +19,8 @@ package com.yahoo.ycsb.generator;
 
 import java.util.Random;
 
+import com.yahoo.ycsb.Utils;
+
 /**
  * A generator of an exponential distribution. It produces a sequence
  * of time intervals (integers) according to an exponential
@@ -43,8 +45,6 @@ public class ExponentialGenerator extends IntegerGenerator
 	 */
 	double _gamma;	
 
-	Random _random;
-
 	/******************************* Constructors **************************************/
 
 	/**
@@ -54,12 +54,10 @@ public class ExponentialGenerator extends IntegerGenerator
 	public ExponentialGenerator(double mean)
 	{
 		_gamma = 1.0/mean;
-		_random = new Random();
 	}
 	public ExponentialGenerator(double percentile, double range)
 	{
 		_gamma = -Math.log(1.0-percentile/100.0) / range;  //1.0/mean;
-		_random = new Random();
 	}
 
 	/****************************************************************************************/
@@ -84,7 +82,7 @@ public class ExponentialGenerator extends IntegerGenerator
 	 */
 	public long nextLong()
 	{
-		return (long) (-Math.log(_random.nextDouble()) / _gamma);
+		return (long) (-Math.log(Utils.random().nextDouble()) / _gamma);
 	}
 
 	@Override
diff --git a/src/com/yahoo/ycsb/generator/HistogramGenerator.java b/src/com/yahoo/ycsb/generator/HistogramGenerator.java
index baaf3588..aba42c47 100644
--- a/src/com/yahoo/ycsb/generator/HistogramGenerator.java
+++ b/src/com/yahoo/ycsb/generator/HistogramGenerator.java
@@ -20,6 +20,8 @@ import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Random;
+
+import com.yahoo.ycsb.Utils;
 import com.yahoo.ycsb.generator.IntegerGenerator;
 
 /**
@@ -41,7 +43,6 @@ public class HistogramGenerator extends IntegerGenerator {
 	long block_size;
 	long[] buckets;
 	long area;
-	Random rand = new Random();
 	long weighted_area = 0;
 	double mean_size = 0;
 	
@@ -93,7 +94,7 @@ public class HistogramGenerator extends IntegerGenerator {
 
 	@Override
 	public int nextInt() {
-		int number = rand.nextInt((int)area);
+		int number = Utils.random().nextInt((int)area);
 		int i;
 		
 		for(i = 0; i < (buckets.length - 1); i++){
diff --git a/src/com/yahoo/ycsb/generator/HotspotIntegerGenerator.java b/src/com/yahoo/ycsb/generator/HotspotIntegerGenerator.java
index 2ede01b9..969ae770 100644
--- a/src/com/yahoo/ycsb/generator/HotspotIntegerGenerator.java
+++ b/src/com/yahoo/ycsb/generator/HotspotIntegerGenerator.java
@@ -18,6 +18,8 @@ package com.yahoo.ycsb.generator;
 
 import java.util.Random;
 
+import com.yahoo.ycsb.Utils;
+
 /**
  * Generate integers resembling a hotspot distribution where x% of operations
  * access y% of data items. The parameters specify the bounds for the numbers,
@@ -37,7 +39,6 @@ public class HotspotIntegerGenerator extends IntegerGenerator {
   private final int coldInterval;
   private final double hotsetFraction;
   private final double hotOpnFraction;
-  private final Random random;
   
   /**
    * Create a generator for Hotspot distributions.
@@ -71,12 +72,12 @@ public class HotspotIntegerGenerator extends IntegerGenerator {
     this.hotInterval = (int)(interval * hotsetFraction);
     this.coldInterval = interval - hotInterval;
     this.hotOpnFraction = hotOpnFraction;
-    random = new Random();
   }
   
   @Override
   public int nextInt() {
     int value = 0;
+    Random random = Utils.random();
     if (random.nextDouble() < hotOpnFraction) {
       // Choose a value from the hot set.
       value = lowerBound + random.nextInt(hotInterval);
diff --git a/src/com/yahoo/ycsb/generator/IntegerGenerator.java b/src/com/yahoo/ycsb/generator/IntegerGenerator.java
index a49d9b47..13d36cd1 100644
--- a/src/com/yahoo/ycsb/generator/IntegerGenerator.java
+++ b/src/com/yahoo/ycsb/generator/IntegerGenerator.java
@@ -31,7 +31,7 @@ public abstract class IntegerGenerator extends Generator
 	 * Set the last value generated. IntegerGenerator subclasses must use this call
 	 * to properly set the last string value, or the lastString() and lastInt() calls won't work.
 	 */
-	public void setLastInt(int last)
+	protected void setLastInt(int last)
 	{
 		lastint=last;
 	}
@@ -54,9 +54,10 @@ public abstract class IntegerGenerator extends Generator
 	 * Calling lastString() should not advance the distribution or have any side effects. If nextString() has not yet 
 	 * been called, lastString() should return something reasonable.
 	 */
+	@Override
 	public String lastString()
 	{
-		return ""+lastint;
+		return ""+lastInt();
 	}
 	
 	/**
diff --git a/src/com/yahoo/ycsb/generator/SkewedLatestGenerator.java b/src/com/yahoo/ycsb/generator/SkewedLatestGenerator.java
index 6d07c5fc..376eb777 100644
--- a/src/com/yahoo/ycsb/generator/SkewedLatestGenerator.java
+++ b/src/com/yahoo/ycsb/generator/SkewedLatestGenerator.java
@@ -28,7 +28,7 @@ public class SkewedLatestGenerator extends IntegerGenerator
 	public SkewedLatestGenerator(CounterGenerator basis)
 	{
 		_basis=basis;
-		_zipfian=new ZipfianGenerator(Integer.parseInt(_basis.lastString()));
+		_zipfian=new ZipfianGenerator(_basis.lastInt());
 		nextInt();
 	}
 
@@ -37,7 +37,7 @@ public class SkewedLatestGenerator extends IntegerGenerator
 	 */
 	public int nextInt()
 	{
-		int max=Integer.parseInt(_basis.lastString());
+		int max=_basis.lastInt();
 		int nextint=max-_zipfian.nextInt(max);
 		setLastInt(nextint);
 		return nextint;
diff --git a/src/com/yahoo/ycsb/generator/UniformIntegerGenerator.java b/src/com/yahoo/ycsb/generator/UniformIntegerGenerator.java
index a6ae08a6..fa6af131 100644
--- a/src/com/yahoo/ycsb/generator/UniformIntegerGenerator.java
+++ b/src/com/yahoo/ycsb/generator/UniformIntegerGenerator.java
@@ -19,12 +19,13 @@ package com.yahoo.ycsb.generator;
 
 import java.util.Random;
 
+import com.yahoo.ycsb.Utils;
+
 /**
  * Generates integers randomly uniform from an interval.
  */
 public class UniformIntegerGenerator extends IntegerGenerator 
 {
-	Random _random;
 	int _lb,_ub,_interval;
 	
 	/**
@@ -35,7 +36,6 @@ public class UniformIntegerGenerator extends IntegerGenerator
 	 */
 	public UniformIntegerGenerator(int lb, int ub)
 	{
-		_random=new Random();
 		_lb=lb;
 		_ub=ub;
 		_interval=_ub-_lb+1;
@@ -44,7 +44,7 @@ public class UniformIntegerGenerator extends IntegerGenerator
 	@Override
 	public int nextInt() 
 	{
-		int ret=_random.nextInt(_interval)+_lb;
+		int ret=Utils.random().nextInt(_interval)+_lb;
 		setLastInt(ret);
 		
 		return ret;
diff --git a/src/com/yahoo/ycsb/generator/ZipfianGenerator.java b/src/com/yahoo/ycsb/generator/ZipfianGenerator.java
index 5a4c0198..8a70f086 100644
--- a/src/com/yahoo/ycsb/generator/ZipfianGenerator.java
+++ b/src/com/yahoo/ycsb/generator/ZipfianGenerator.java
@@ -19,6 +19,8 @@ package com.yahoo.ycsb.generator;
 
 import java.util.Random;
 
+import com.yahoo.ycsb.Utils;
+
 /**
  * A generator of a zipfian distribution. It produces a sequence of items, such that some items are more popular than others, according
  * to a zipfian distribution. When you construct an instance of this class, you specify the number of items in the set to draw from, either
@@ -61,8 +63,6 @@ public class ZipfianGenerator extends IntegerGenerator
 	 */
 	double alpha,zetan,eta,theta,zeta2theta;
 	
-	Random random;
-
 	/**
 	 * The number of items used to compute zetan the last time.
 	 */
@@ -136,8 +136,6 @@ public class ZipfianGenerator extends IntegerGenerator
 		base=min;
 		zipfianconstant=_zipfianconstant;
 
-		random=new Random();	 
-
 		theta=zipfianconstant;
 
 		zeta2theta=zeta(2,theta);
@@ -273,7 +271,7 @@ public class ZipfianGenerator extends IntegerGenerator
 			}
 		}
 
-		double u=random.nextDouble();
+		double u=Utils.random().nextDouble();
 		double uz=u*zetan;
 
 		if (uz<1.0)
-- 
GitLab