jit会优化新对象吗? [英] Will the jit optimize new objects

查看:98
本文介绍了jit会优化新对象吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我创建了这个类,因为它是不可变的并且具有流畅的API:

I created this class for being immutable and having a fluent API:

public final class Message {
    public final String email;
    public final String escalationEmail;
    public final String assignee;
    public final String conversationId;
    public final String subject;
    public final String userId;

    public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
        this.email = email;
        this.escalationEmail = escalationEmail;
        this.assignee = assignee;
        this.conversationId = conversationId;
        this.subject = subject;
        this.userId = userId;
    }

    public Message() {
        email = "";
        escalationEmail = "";
        assignee = "";
        conversationId = "";
        subject = "";
        userId = "";
    }

    public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
    public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
    public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
    public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
    public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
    public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }

}

我的问题是,优化器是否能够避免创建新对象时有很多对象创建:

My question is, will the optimizer be able to avoid lots of object creations when a new object is created like this:

Message m = new Message()
    .email("foo@bar.com")
    .assignee("bar@bax.com")
    .subject("subj");

生成单独的可变构建器对象有什么好处吗?

Is there anything to be gained from making a separate mutable builder object instead?

更新2:
在阅读了apangin的答案后,我的基准测试无效。我会把它留在这里作为参考如何不进行基准测试:)

Update 2: After reading apangin's answer my benchmark is invalidated. I'll keep it here for reference of how not to benchmark :)

更新:
我冒昧地测量了这个我自己使用这段代码:

Update: I took the liberty of measuring this myself with this code:

public final class Message {
public final String email;
public final String escalationEmail;
public final String assignee;
public final String conversationId;
public final String subject;
public final String userId;

public static final class MessageBuilder {
    private String email;
    private String escalationEmail;
    private String assignee;
    private String conversationId;
    private String subject;
    private String userId;

    MessageBuilder email(String e) { email = e; return this; }
    MessageBuilder escalationEmail(String e) { escalationEmail = e; return this; }
    MessageBuilder assignee(String e) { assignee = e; return this; }
    MessageBuilder conversationId(String e) { conversationId = e; return this; }
    MessageBuilder subject(String e) { subject = e; return this; }
    MessageBuilder userId(String e) { userId = e; return this; }

    public Message create() {
        return new Message(email, escalationEmail, assignee, conversationId, subject, userId);
    }

}

public static MessageBuilder createNew() {
    return new MessageBuilder();
}

public Message(String email, String escalationEmail, String assignee, String conversationId, String subject, String userId) {
    this.email = email;
    this.escalationEmail = escalationEmail;
    this.assignee = assignee;
    this.conversationId = conversationId;
    this.subject = subject;
    this.userId = userId;
}

public Message() {
    email = "";
    escalationEmail = "";
    assignee = "";
    conversationId = "";
    subject = "";
    userId = "";
}

public Message email(String e) { return new Message(e, escalationEmail, assignee, conversationId, subject, userId); }
public Message escalationEmail(String e) { return new Message(email, e, assignee, conversationId, subject, userId); }
public Message assignee(String a) { return new Message(email, escalationEmail, a, conversationId, subject, userId); }
public Message conversationId(String c) { return new Message(email, escalationEmail, assignee, c, subject, userId); }
public Message subject(String s) { return new Message(email, escalationEmail, assignee, conversationId, s, userId); }
public Message userId(String u) { return new Message(email, escalationEmail, assignee, conversationId, subject, u); }


static String getString() {
    return new String("hello");
    // return "hello";
}

public static void main(String[] args) {
    int n = 1000000000;

    long before1 = System.nanoTime();

    for (int i = 0; i < n; ++i) {
        Message m = new Message()
                .email(getString())
                .assignee(getString())
                .conversationId(getString())
                .escalationEmail(getString())
                .subject(getString())
                .userId(getString());
    }

    long after1 = System.nanoTime();

    long before2 = System.nanoTime();

    for (int i = 0; i < n; ++i) {
        Message m = Message.createNew()
                .email(getString())
                .assignee(getString())
                .conversationId(getString())
                .escalationEmail(getString())
                .subject(getString())
                .userId(getString())
                .create();
    }

    long after2 = System.nanoTime();



    System.out.println("no builder  : " + (after1 - before1)/1000000000.0);
    System.out.println("with builder: " + (after2 - before2)/1000000000.0);
}


}

我发现了如果字符串参数不是新对象,但是所有相同(请参阅getString中的注释代码)

I found the difference to be significant (builder is faster) if the string arguments are not new objects, but all the same (see commented code in getString)

我认为是一个差异是重要的(构建器更快)更现实的情况是,当所有字符串都是新对象时,差异可以忽略不计,JVM启动会导致第一个字符串变慢(我尝试了两种方式)。

In what I imagine is a more realistic scenario, when all the strings are new objects, the difference is negligible, and the JVM startup would cause the first one to be a tiny bit slower (I tried both ways).

使用new String代码总共慢了很多倍(我不得不减少 n ),这可能表明存在一些优化新消息正在进行,但不是新字符串。

With the "new String" the code was in total many times slower as well (I had to decrease the n), perhaps indicating that there is some optimization of the "new Message" going on, but not of the "new String".

推荐答案

是的,HotSpot JIT可以消除冗余分配在本地环境中。

Yes, HotSpot JIT can eliminate redundant allocations in a local context.

此优化由 JDK 6u23后启用了转义分析。它经常与堆栈分配混淆,但实际上它更强大,因为它不仅允许在堆栈上分配对象,而且通过用变量(标量替换)替换对象字段来完全消除分配。优化。

This optimization is provided by the Escape Analysis enabled since JDK 6u23. It is often confused with on-stack allocation, but in fact it is much more powerful, since it allows not only to allocate objects on stack, but to eliminate allocation altogether by replacing object fields with variables (Scalar Replacement) that are subject to further optimizations.

优化由 -XX:+ EliminateAllocations JVM选项控制,默认情况下为ON。

The optimization is controlled by -XX:+EliminateAllocations JVM option which is ON by default.

感谢分配消除优化,您创建消息的示例对象以同样的方式有效地工作。他们不分配中间对象;只是最后一个。

Thanks to allocation elimination optimization, both your examples of creating a Message object work effectively the same way. They do not allocate intermediate objects; just the final one.

您的基准显示误导性结果,因为它收集了很多微基准测试的常见缺陷

Your benchmark shows misleading results, because it collects many common pitfalls of microbenchmarking:


  • 它包含了几个单一方法中的基准;

  • 它测量 OSR存根而不是最终编译版本;

  • 它不进行预热迭代;

  • 它不消耗结果等。

  • it incorporates several benchmarks in a single method;
  • it measures an OSR stub instead of the final compiled version;
  • it does not do warm-up iterations;
  • it does not consume results, etc.

让我们用 JMH 。作为奖励,JMH具有分配探查器( -prof gc ),其显示每次迭代实际分配的字节数。我添加了第三个测试,该测试运行时禁用 EliminateAllocations 优化以显示差异。

Let's measure it correctly with JMH. As a bonus, JMH has the allocation profiler (-prof gc) which shows how many bytes are really allocated per iteration. I've added the third test that runs with EliminateAllocations optimization disabled to show the difference.

package bench;

import org.openjdk.jmh.annotations.*;

@State(Scope.Benchmark)
public class MessageBench {

    @Benchmark
    public Message builder() {
        return Message.createNew()
                .email(getString())
                .assignee(getString())
                .conversationId(getString())
                .escalationEmail(getString())
                .subject(getString())
                .userId(getString())
                .create();
    }

    @Benchmark
    public Message immutable() {
        return new Message()
                .email(getString())
                .assignee(getString())
                .conversationId(getString())
                .escalationEmail(getString())
                .subject(getString())
                .userId(getString());
    }

    @Benchmark
    @Fork(jvmArgs = "-XX:-EliminateAllocations")
    public Message immutableNoOpt() {
        return new Message()
                .email(getString())
                .assignee(getString())
                .conversationId(getString())
                .escalationEmail(getString())
                .subject(getString())
                .userId(getString());
    }

    private String getString() {
        return "hello";
    }
}

以下是结果。 builder immutable 同样执行,每次迭代只分配40个字节(正好是一个消息对象)。

Here are the results. Both builder and immutable perform equally and allocate just 40 bytes per iteration (exactly the size of one Message object).

Benchmark                                        Mode  Cnt     Score     Error   Units
MessageBench.builder                             avgt   10     6,232 ±   0,111   ns/op
MessageBench.immutable                           avgt   10     6,213 ±   0,087   ns/op
MessageBench.immutableNoOpt                      avgt   10    41,660 ±   2,466   ns/op

MessageBench.builder:·gc.alloc.rate.norm         avgt   10    40,000 ±   0,001    B/op
MessageBench.immutable:·gc.alloc.rate.norm       avgt   10    40,000 ±   0,001    B/op
MessageBench.immutableNoOpt:·gc.alloc.rate.norm  avgt   10   280,000 ±   0,001    B/op

这篇关于jit会优化新对象吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆