linux 延时一微秒
低延迟Java应用程序中的许多基准测试涉及必须在一定负载下测量系统。 这就要求保持事件进入系统的稳定吞吐量,而不是在没有任何控制的情况下以全油门将事件泵入系统。
我经常要做的任务之一是在事件之间将生产者线程暂停一小段时间。 通常,此时间量为个位数微秒。
那么,如何在此时间内暂停线程? 大多数Java开发人员会立即想到Thread.sleep()
。 但这是行不通的,因为Thread.sleep()
仅下降到毫秒,并且比我们暂停所需的时间(以微秒为单位)长一个数量级。
我在StackOverflow上看到一个答案,将用户指向TimeUnit.MICROSECONDS.sleep()
以便睡眠少于一毫秒。 引用JavaDoc ,这显然是不正确的:
使用此时间单位执行
Thread.sleep
。 这是一种方便的方法,可以将时间参数转换为Thread.sleep
方法所需的形式。
因此,您将无法获得比Thread.sleep(1)
相似的1毫秒的暂停。 (您可以在下面的代码中尝试示例来证明这一点)。
这样做的原因是这种暂停方法(即使线程进入睡眠状态并唤醒它)永远不会足够快或准确到不足一毫秒。
此时我们要介绍的另一个问题是Thread.sleep(1)
到底有多精确? 稍后我们将再次讨论。
当我们想暂停一微秒时,另一个选择是使用LockSupport.parkNanos(x)
。 使用以下代码停泊1微秒实际上需要约10us。 它比TimeUnit.sleep()/ Thread.sleep()更好,但并不真正适合目的。 100us之后,它确实会以50%的变化进入同一个球场。
package nanotime;import java.util.Arrays;
import java.util.concurrent.TimeUnit;/*** Created by daniel on 28/10/2015.*/
public class NanoTimer {public static void main(String[] args) throws InterruptedException {long[] samples = new long[100_000];int pauseInMillis = 1;for (int i = 0; i < samples.length; i++) {long firstTime = System.nanoTime();LockSupport.parkNanos(pauseInMicros);long timeForNano = System.nanoTime() - firstTime;samples[i] = timeForNano;}System.out.printf("Time for LockSupport.parkNanos() %.0f\n", Arrays.stream(samples).average().getAsDouble());}
}
解决我们问题的方法是使用System.nanoTime() 。 通过忙于等待对System.nanoTime
的调用,我们将能够暂停一微秒。 我们将在一秒钟内看到此代码,但首先让我们了解System.nanosecond()
的准确性。 至关重要的是,执行对System.nanoSecond()
的调用需要多长时间。
这是一些可以完全做到这一点的代码:
package nanotime;public class NanoTimer {public static void main(String[] args) throws InterruptedException {long[] samples = new long[1_000_000];for (int i = 0; i < samples.length; i++) {long firstTime = System.nanoTime();long timeForNano = System.nanoTime() - firstTime;samples[i] = timeForNano;}System.out.printf("Time for call to nano %.0f nanseconds", Arrays.stream(samples).average().getAsDouble());}
}
在我的MBP上,从一台机器到另一台机器,数字将有所不同,大约为40纳秒。
这告诉我们,我们应该能够测量大约40纳秒的精度。 因此,应该很容易测量到1微秒(1000纳秒)。
这是忙碌的等待方法,“暂停”了微秒:
package nanotime;import java.util.Arrays;
/*** Created by daniel on 28/10/2015.*/
public class NanoTimer {public static void main(String[] args) throws InterruptedException {long[] samples = new long[100_000];int pauseInMicros = 1;for (int i = 0; i < samples.length; i++) {long firstTime = System.nanoTime();busyWaitMicros(pauseInMicros);long timeForNano = System.nanoTime() - firstTime;samples[i] = timeForNano;}System.out.printf("Time for micro busyWait %.0f\n", Arrays.stream(samples).average().getAsDouble());}public static void busyWaitMicros(long micros){long waitUntil = System.nanoTime() + (micros * 1_000);while(waitUntil > System.nanoTime()){;}}
}
该代码等待一微秒,然后乘以等待时间。 在我的机器上,我得到1,115纳秒,准确度在90%左右。
当您等待更长的时间时,精度会提高,10毫秒需要10,267纳秒,即97%的准确度,而100毫秒需要100,497纳秒,即99.5%的准确度。
那么Thread.sleep(1)
到底有多精确?
这是代码:
package nanotime;import java.util.Arrays;
import java.util.concurrent.TimeUnit;/*** Created by daniel on 28/10/2015.*/
public class NanoTimer {public static void main(String[] args) throws InterruptedException {long[] samples = new long[100_000];int pauseInMillis = 1;for (int i = 0; i < samples.length; i++) {long firstTime = System.nanoTime();Thread.sleep(pauseInMicros);long timeForNano = System.nanoTime() - firstTime;samples[i] = timeForNano;}System.out.printf("Time for micro sleep %.0f\n", Arrays.stream(samples).average().getAsDouble());}
}
1毫秒睡眠的平均时间(以纳秒为单位)为1,295,509。 准确率只有〜75%。 对于几乎所有内容,它可能已经足够好了,但是如果您想要精确的毫秒级暂停,那么忙碌的等待会更好。 当然,您需要记住繁忙的等待,按照定义,繁忙的等待会使您的线程繁忙,并会花费您CPU的时间。
汇总表
暂停方式 | 1us | 10us | 100us | 1000us / 1ms | 10,000us / 10ms |
---|---|---|---|---|---|
TimeUnit.Sleep() | 1284.6 | 1293.8 | 1295.7 | 1292.7 | 11865.3 |
LockSupport.parkNanos() | 8.1 | 28.4 | 141.8 | 1294.3 | 11834.2 |
忙等待 | 1.1 | 10.1 | 100.2 | 1000.2 | 10000.2 |
结论
- 如果您想暂停不到一毫秒,则需要忙于等待
- System.nanoSecond()大约需要40ns
- Thread.sleep(1)的准确率只有75%
- 忙于等待超过10us或更高的时间几乎是100%准确
- 繁忙的等待将占用CPU
翻译自: https://www.javacodegeeks.com/2015/11/lets-pause-for-a-microsecond.html
linux 延时一微秒