如何减少Java的抖动?

24
为了解决这个问题,我创建了一个开源的Java线程亲和库
当我有一些紧密交互的线程时,它可以减少延迟并增加吞吐量。对于单线程任务,它仍然可以大幅减少抖动。

这个程序查看System.nanoTime()调用之间的时间差,并报告超过10x,000 ns的差异。

public class TimeJumpingMain {
    static final long IGNORE_TIME = 1000 * 1000 * 1000; // the first second to allow warmup.
    static final int minJump = 10; // smallest jump of 10 us.
    static final int midJump = 100; // mid size jump of 100 us.
    static final int bigJump = 1000; // big jump of 1 ms.

    public static void main(String... args) {
        int[] intervalTimings = new int[1000];
        int[] jumpTimings = new int[1000];

        long start = System.nanoTime();
        long prev = start;
        long prevJump = start;
        int jumpCount = 0;
        int midJumpCount = 0;
        int bigJumpCount = 0;

        while (true) {
            long now = System.nanoTime();
            long jump = (now - prev) / 1000;
            if (jump > minJump && now - start > IGNORE_TIME) {
                long interval = (now - prevJump) / 1000;
                if (jumpCount < intervalTimings.length) {
                    intervalTimings[jumpCount] = (int) interval;
                    jumpTimings[jumpCount] = (int) jump;
                }
                if (jump >= midJump)
                    midJumpCount++;
                if (jump >= bigJump)
                    bigJumpCount++;
                prevJump = now;
                jumpCount++;
            }
            prev = now;
            if (now - start > 120L * 1000 * 1000 * 1000 + IGNORE_TIME)
                break;
        }
        System.out.println("interval us\tdelay us");
        for (int i = 0; i < jumpCount && i < intervalTimings.length; i++) {
            System.out.println(intervalTimings[i] + "\t" + jumpTimings[i]);
        }
        System.out.printf("Time jumped %,d / %,d / %,d times by at least %,d / %,d / %,d us in %.1f seconds %n",
                jumpCount, midJumpCount, bigJumpCount, minJump, midJump, bigJump, (System.nanoTime() - start - IGNORE_TIME) / 1e9);
    }
}

在我的机器上,这报告如下。
Time jumped 2,905 / 131 / 20 times by at least 10 / 100 / 1,000 us in 120.0 seconds   

我曾尝试使用chrt来设置实时优先级,以及使用taskset在启动进程后尝试锁定到单个核心,但这些并没有像我预期的那样有所帮助。
我将所有中断移动到了CPU 0-3,并将所有进程的CPU掩码从0xFF更改为0x0F。在top中,前四个CPU大约99%空闲,而最后四个CPU则是100.0%空闲。
作为root用户,使用chrt -r 99
Time jumped 673 / 378 / 44 times by at least 10 / 100 / 1,000 us in 120.0 seconds 

然而,当单独使用 taskset -c 7 时(我已确保cpu7空闲)

Time jumped 24 / 1 / 0 times by at least 10 / 100 / 1,000 us in 120.0 seconds 

使用 chrt - r 99 taskset -c 7 命令

Time jumped 7 / 1 / 0 times by at least 10 / 100 / 1,000 us in 120.0 seconds  

看起来在进程启动后尝试使用taskset并不起作用。

更广泛的问题是:

如何减少Java进程的抖动?还有没有其他减少Linux上抖动的技巧?

注意:此进程运行期间不会发生GC(使用-verbosegc进行了检查)

似乎代码编译可能会导致每次延迟3.62毫秒,每100-102毫秒一次。因此,我忽略第一秒钟中的所有内容作为预热。


4
尽量不要在热循环中打印内容,而是将结果添加到ArrayList中以减少系统调用。你还检查了编译器是否起作用吗? - bestsss
1
似乎在进程启动后尝试使用taskset并不能起作用。我从未成功过(在进程启动后),所以如果我需要CPU亲和力,我会依赖于预先选择它。 - bestsss
1个回答

17

系统抖动和JVM抖动都存在。

对于前者,您可以在启动时使用isolcpus参数来确保除了应用程序代码之外没有其他内容可以在这些CPU上运行。

http://www.novell.com/support/viewContent.do?externalId=7009596&sliceId=1

理想情况下,您可以通过jni调用(到自己的jni库)到sched_setaffinity以针对活动线程进行设置,以便只有该线程在那里运行。

根据我的经验,使用isolcpus并将中断仅由特定核心处理,关闭超线程并删除绝对所有电源管理(这些是BIOS选项,当它们可用时,可关闭所有的C状态和P状态管理),同时在受屏蔽的核心上运行应用程序可以最小化系统抖动。当然,BIOS特定选项具体取决于您的主板型号,因此您需要根据您的主板型号进行调查。

在系统级别上要注意的另一件事是本地APIC中断(LOC,本地中断计数器)频率。这是一个“低延迟桌面”使用1kHz中断吗?无论哪种方式,您都可以期望抖动聚集在中断间隔周围。

我还知道另外两个抖动的源,但对它们几乎一无所知;内核TLB刷新中断和用户空间TLB刷新中断。某些RT内核提供了控制这些选项的选项,因此这可能是需要查看的另一件事。您还可以查看此站点以获取有关在RT内核上构建RT应用程序的更多提示。


LOC的频率为100 Hz。如果有关闭它们的方法,我会非常感兴趣,然而它们似乎并没有造成太多开销。你在CentOS上试过这个吗?http://editthis.info/wiki/User:Ascensus (你是这里的Matt吗?)有关于在CentOS中使用isolcpus的建议吗? - Peter Lawrey
@PeterLawrey(和Matt),除了“操作系统”强加的峰值之外,Java和RT是一种难以处理的语言,因为编译器和GC需要以更高的优先级运行,但通常在Linux上不可用。虽然IBM正在努力运行微秒GC周期,但预热所有代码路径以使编译器不会(去)优化任何内容可能会很困难。 - bestsss
不同的问题。我还没有在CentOS上尝试过,假设您有一个SMP内核,并且在启动时确保将isolcpus设置为内核参数,那么它应该可以正常工作。当然,你的情况可能会有所不同!在100Hz下的LOC开销应该很小,但由于此结果,您可能会每10ms获得抖动,您无法关闭它(据我所知)。 - Matt
我有一个脚本,它可以设置所有进程的taskset并设置中断的smp_affinity。它似乎可以在不重新启动系统的情况下达到与isolcpus相同的效果。一旦我对此满意,这个选项听起来是设置重启后仍然生效的最佳方式。 - Peter Lawrey

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接