diff --git a/core/src/main/java/com/yahoo/ycsb/Client.java b/core/src/main/java/com/yahoo/ycsb/Client.java index 80a4a86f6800fa0f9d21e036d3fe20c3ecc36634..6f4bd56f1c92c2e5c6380417332c42beebd46f90 100644 --- a/core/src/main/java/com/yahoo/ycsb/Client.java +++ b/core/src/main/java/com/yahoo/ycsb/Client.java @@ -28,6 +28,7 @@ import java.util.Date; import java.util.Enumeration; import java.util.Properties; import java.util.Vector; +import java.util.concurrent.locks.LockSupport; import com.yahoo.ycsb.measurements.Measurements; import com.yahoo.ycsb.measurements.exporter.MeasurementsExporter; @@ -144,13 +145,14 @@ class ClientThread extends Thread boolean _dotransactions; Workload _workload; int _opcount; - double _target; + double _targetOpsPerMs; int _opsdone; int _threadid; int _threadcount; Object _workloadstate; Properties _props; + private long _targetOpsTickNs; /** @@ -173,7 +175,10 @@ class ClientThread extends Thread _workload=workload; _opcount=opcount; _opsdone=0; - _target=targetperthreadperms; + if(targetperthreadperms > 0){ + _targetOpsPerMs=targetperthreadperms; + _targetOpsTickNs=(long)(1000000/_targetOpsPerMs); + } _threadid=threadid; _threadcount=threadcount; _props=props; @@ -209,26 +214,22 @@ class ClientThread extends Thread return; } - //spread the thread operations out so they don't all hit the DB at the same time - try - { - //GH issue 4 - throws exception if _target>1 because random.nextInt argument must be >0 - //and the sleep() doesn't make sense for granularities < 1 ms anyway - if ( (_target>0) && (_target<=1.0) ) - { - sleep(Utils.random().nextInt((int)(1.0/_target))); - } - } - catch (InterruptedException e) - { - // do nothing. - } + //NOTE: Switching to using nanoTime and parkNanos for time management here such that the measurements + // and the client thread have the same view on time. + //spread the thread operations out so they don't all hit the DB at the same time + // GH issue 4 - throws exception if _target>1 because random.nextInt argument must be >0 + // and the sleep() doesn't make sense for granularities < 1 ms anyway + if ((_targetOpsPerMs > 0) && (_targetOpsPerMs <= 1.0)) + { + long randomMinorDelay = Utils.random().nextInt((int) _targetOpsTickNs); + sleepUntil(System.nanoTime() + randomMinorDelay); + } try { if (_dotransactions) { - long st=System.currentTimeMillis(); + long startTimeNanos = System.nanoTime(); while (((_opcount == 0) || (_opsdone < _opcount)) && !_workload.isStopRequested()) { @@ -240,13 +241,13 @@ class ClientThread extends Thread _opsdone++; - throttle(st); + throttleNanos(startTimeNanos); } } else { - long st=System.currentTimeMillis(); - + long startTimeNanos = System.nanoTime(); + while (((_opcount == 0) || (_opsdone < _opcount)) && !_workload.isStopRequested()) { @@ -257,7 +258,7 @@ class ClientThread extends Thread _opsdone++; - throttle(st); + throttleNanos(startTimeNanos); } } } @@ -280,27 +281,22 @@ class ClientThread extends Thread } } - private void throttle(long currTimeMillis) { + private void sleepUntil(long deadline) { + long now = System.nanoTime(); + while((now = System.nanoTime()) < deadline) { + LockSupport.parkNanos(deadline - now); + } + } + private long throttleNanos(long startTimeNanos) { //throttle the operations - if (_target>0) + if (_targetOpsPerMs > 0) { - //this is more accurate than other throttling approaches we have tried, - //like sleeping for (1/target throughput)-operation latency, - //because it smooths timing inaccuracies (from sleep() taking an int, - //current time in millis) over many operations - while (System.currentTimeMillis()-currTimeMillis<((double)_opsdone)/_target) - { - try - { - sleep(1); - } - catch (InterruptedException e) - { - // do nothing. - } - - } + // delay until next tick + long deadline = startTimeNanos + _opsdone*_targetOpsTickNs; + sleepUntil(deadline); + return deadline; } + return -1; } }