繁体   English   中英

分配延迟似乎很高,为什么?

[英]Allocation latency seems high, why?

我有一个在低延迟环境中运行的(java)应用程序,它通常处理大约600微米(+/- 100)的指令。 当然,随着我们进一步进入微秒空间,您看到的成本延迟会发生变化,现在我们已经注意到,2/3的时间用于分配2个核心域对象。

基准测试已将代码的违规部分与现有引用中的对象构造完全隔离,即基本上是一组引用(每个类中约15个)和一些新的列表,但请参阅下面关于确切测量的内容的注释这里。

每个人一直需要~100微米,这对我来说是莫名其妙的,我试图找出原因。 一个快速的基准测试表明,一个类似大小的对象充满了大约需要2-3微米的新对象,显然这种基准测试充满了困难,但认为它可能有用作基线。

这里有2个Q.

  • 如何调查这种行为?
  • 缓慢分配的解释是什么?

请注意,所涉及的硬件是Sun X4600上的Solaris 10 x86,带有8 *双核opteron @ 3.2GHz

我们看过的东西包括

  • 检查PrintTLAB统计信息,显示一些缓慢的分配,因此不存在争用。
  • PrintCompilation建议这些代码中的一个不是JIT友好的,虽然Solaris似乎在这里有一些不寻常的行为(即对现代的linux,没有与solaris10类似的老式的Linux现在就可以进行替换)
  • LogCompilation ...有点难以解析,至少可以说这是一项持续的工作,到目前为止还没有什么明显的
  • JVM版本......在6u6和6u14中保持一致,尚未尝试6u18或最新版本7

任何和所有的想法赞赏

关于各种帖子的评论摘要,试图让事情更清楚

  • 我测量的成本是创造,它通过生成器(像一个内置对象的总成本这些 ),其私人构造函数调用新的ArrayList几次以及对现有对象设置引用。 测量的成本包括设置构建器的成本以及构建器到域对象的转换
  • 编译(通过热点)有明显的影响,但它仍然相对较慢(在这种情况下编译从100micros到~60)
  • 在我的天真基准测试中编译(通过热点)需要将分配时间从~2micros降低到~300ns
  • 延迟不会因年轻的收集算法(ParNew或平行清除)而有所不同

既然你的问题更多的是关于如何调查问题而不是“我的问题是什么”,那么我会坚持使用一些工具来尝试。

一个非常有用的工具,可以更好地了解BTrace的内容和时间。 它类似于DTrace,但是纯Java工具。 在那个笔记上我假设你知道DTrace,如果不是,那也是有用的,如果不是钝的。 这些将为您提供有关JVM和OS中发生的事情以及何时的可见性。

哦,在你的原始帖子中澄清另一件事。 你在跑什么收藏家? 我假设您遇到高延迟问题,您正在使用像CMS这样的低暂停收集器。 如果是这样,你尝试过任何调整?

当您多次重复相同的任务时,您的CPU往往会非常有效地运行。 这是因为您的缓存未命中时间和CPU的预热并不是一个因素。 你也可能不考虑你的JVM温暖时间。

如果在JVM和/或CPU未预热时尝试相同的操作。 你会得到一个非常不同的结果。

尝试在测试之间执行相同的操作25次(小于编译阈值)和睡眠(100)。 您应该期望看到更高的时间,更接近您在实际应用程序中看到的内容。

您的应用的行为会有所不同,但说明我的观点。 我发现等待IO可能比普通睡眠更具破坏性。

当您执行基准测试时,您应该尝试确保您比较喜欢。

import java.io.*;
import java.util.Date;

/**
Cold JVM with a Hot CPU took 123 us average
Cold JVM with a Cold CPU took 403 us average
Cold JVM with a Hot CPU took 314 us average
Cold JVM with a Cold CPU took 510 us average
Cold JVM with a Hot CPU took 316 us average
Cold JVM with a Cold CPU took 514 us average
Cold JVM with a Hot CPU took 315 us average
Cold JVM with a Cold CPU took 545 us average
Cold JVM with a Hot CPU took 321 us average
Cold JVM with a Cold CPU took 542 us average
Hot JVM with a Hot CPU took 44 us average
Hot JVM with a Cold CPU took 111 us average
Hot JVM with a Hot CPU took 32 us average
Hot JVM with a Cold CPU took 96 us average
Hot JVM with a Hot CPU took 26 us average
Hot JVM with a Cold CPU took 80 us average
Hot JVM with a Hot CPU took 26 us average
Hot JVM with a Cold CPU took 90 us average
Hot JVM with a Hot CPU took 25 us average
Hot JVM with a Cold CPU took 98 us average
 */
public class HotColdBenchmark {
    public static void main(String... args) {
        // load all the classes.
        performTest(null, 25, false);
        for (int i = 0; i < 5; i++) {
            // still pretty cold
            performTest("Cold JVM with a Hot CPU", 25, false);
            // still pretty cold
            performTest("Cold JVM with a Cold CPU", 25, true);
        }

        // warmup the JVM
        performTest(null, 10000, false);
        for (int i = 0; i < 5; i++) {
            // warmed up.
            performTest("Hot JVM with a Hot CPU", 25, false);
            // bit cold
            performTest("Hot JVM with a Cold CPU", 25, true);
        }
    }

    public static long performTest(String report, int n, boolean sleep) {
        long time = 0;
        long ret = 0;
        for (int i = 0; i < n; i++) {
            long start = System.nanoTime();
            try {
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(baos);
                oos.writeObject(new Date());
                oos.close();
                ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
                Date d = (Date) ois.readObject();
                ret += d.getTime();
                time += System.nanoTime() - start;
                if (sleep) Thread.sleep(100);
            } catch (Exception e) {
                throw new AssertionError(e);
            }
        }
        if (report != null) {
            System.out.printf("%s took %,d us average%n", report, time / n / 1000);
        }
        return ret;
    }
}

内存分配可能会导致副作用。 是否有可能内存分配导致堆被压缩? 您是否查看了内存分配是否导致GC同时运行?

您是否单独计算了创建新ArrayLists所需的时间?

对于在通用操作系统上运行的通用VM,即使使用如此出色的硬件,也没有希望获得微秒延迟保证。 大容量吞吐量是您所希望的最佳选择。 如果需要,可以切换到实时虚拟机(我正在谈论RTSJ以及所有 ......)

......我的两分钱

只是一些疯狂的猜测:

我的理解是,Java VM以不同于长期对象的方式处理短期对象的内存。 对我来说似乎是合理的,在一个对象从一个函数本地引用变为在全局堆中引用的那一点上将是一件大事。 它现在必须由GC跟踪,而不是在功能退出时可用于清理。

或者可能是从一个引用到多个引用到单个对象必须更改GC记帐。 只要对象具有单个引用,就很容易清理。 多个引用可以具有引用循环和/或GC可能必须在所有其他对象中搜索引用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM