gpt4 book ai didi

java - 如何使用 System.nanoTime() 准确延迟循环迭代以达到每秒 1M 之类的频率?

转载 作者:行者123 更新时间:2023-12-04 09:47:58 25 4
gpt4 key购买 nike

我创建了一个 Java 程序来以特定频率发出事件。我正在使用 System.nanoTime()而不是 Thread.sleep()因为根据许多引用文献 here,第一个给出了更高的区间精度和 here .但是,我想当我尝试将其设置为发出 1M 记录/秒的数据速率时,它并没有达到目标。这是我的代码:

long delayInNanoSeconds = 1000000;

private void generateTaxiRideEvent(SourceContext<TaxiRide> sourceContext) throws Exception {
gzipStream = new GZIPInputStream(new FileInputStream(dataFilePath));
reader = new BufferedReader(new InputStreamReader(gzipStream, StandardCharsets.UTF_8));
String line;
TaxiRide taxiRide;
while (reader.ready() && (line = reader.readLine()) != null) {
taxiRide = TaxiRide.fromString(line);
sourceContext.collectWithTimestamp(taxiRide, getEventTime(taxiRide));
// sleep in nanoseconds to have a reproducible data rate for the data source
this.dataRateListener.busySleep();
}
}

public void busySleep() {
final long startTime = System.nanoTime();
while ((System.nanoTime() - startTime) < this.delayInNanoSeconds) ;
}

所以,当我在 delayInNanoSeconds 中等待 10000 纳秒时变量我会得到 100K 的工作量
记录/秒(1_000_000_000/10_000 = 100K 转/秒)。当我在 delayInNanoSeconds 中等待 2000 纳秒时变量我会得到一个工作量
500K 记录/秒(1_000_000_000/2_000 = 500K 转/秒)。对于 1000 纳秒,我将获得 1M 的工作负载
记录/秒(1_000_000_000/1000 = 1M r/s)。对于 500 纳秒,2M rec/sec (1_000_000_000/500 = 2M r/s) 的工作负载。

我看到了 here最好使用 double而不是 long以提高精度。它有什么关系吗?或者问题只是操作系统限制(我使用的是 Linux Ubuntu 18)?或者也许是因为我正在使用 readLine()方法,有没有更快的方法来发出这些事件?我认为当我使用 GZIPInputStream 时我正在将整个文件加载到内存中,而 readLine()不再访问磁盘。如何提高应用程序的数据速率?

最佳答案

@TobiasGeiselmann 提出了一个很好的观点:您的延迟计算没有考虑调用 busySleep 之间花费的时间。

您应该计算相对于上一个截止日期的截止日期 ,不是登录后的当前时间。不要使用之前 System.nanoTime() 的结果任何一个;这将是一段时间 >= 实际截止日期(因为 nanoTime 本身需要时间,至少需要几纳秒,因此它不可避免地会睡过头)。你会以这种方式累积错误。

在第一次迭代之前,找到当前时间并设置long deadline = System.nanoTime(); .在每次迭代结束时,做 deadline += 1000;并使用您的忙等待循环旋转直到现在 >= 截止日期。

deadline - now足够大,使用一些可以将 CPU 交给其他线程的东西,直到接近唤醒期限 .根据评论,LockSupport.parkNanos(…)对于现代 Java 来说是一个不错的选择,实际上可能会忙着等待足够短的 sleep (?)。我真的不知道Java。如果是这样,您应该只检查当前时间,计算到截止日期的时间,然后调用一次。

(对于像 Intel Tremont(下一代 Goldmont)这样的 future CPU,LockSupport.parkNanos 可以可移植地公开像 tpause 这样的功能来空闲 CPU 核心直到给定的 TSC 截止日期。不是通过操作系统,只是一个超线程友好的截止期限暂停,很好用于在 SMT CPU 上短暂 hibernate 。)

忙等待通常是不好的,但适用于高精度非常短的延迟。在具有当前操作系统的当前硬件上,1 微秒不足以有效地让操作系统上下文切换到其他内容并返回。但是更长的 sleep 间隔(当您选择较低的频率时)应该 sleep 让操作系统在这个核心上做一些有用的事情,而不是只是忙着等待这么长时间。

理想情况下,当您进行时间检查时,您将执行像 x86 的 pause 这样的指令。在延迟循环中,对共享相同物理内核的其他逻辑内核更加友好(超线程/SMT)。 Java 9 Thread.onSpinWait();应该在自旋等待循环中调用(尤其是在等待内存时),这让 JVM 以可移植的方式公开这个概念。 (我想这就是它的用途。)

如果您的系统足够快,可以在每次迭代运行该时间获取函数时跟上,那么这将起作用。如果没有,那么您可以每 4 次迭代(循环展开)检查一个截止日期,以分摊 nanoTime() 的成本。所以你登录 4 或什么的突发。

当然,如果您的系统即使根本没有延迟调用也不够快,您需要优化一些东西来解决这个问题。您不能延迟负的时间,检查时钟本身需要时间。

关于java - 如何使用 System.nanoTime() 准确延迟循环迭代以达到每秒 1M 之类的频率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62061643/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com