mor*_*ten 4 java jit fluent immutability compiler-optimization
我创建了这个类是不可变的,并且具有流畅的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); }
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,当像这样创建一个新对象时,优化器是否能够避免大量的对象创建:
Message m = new Message()
.email("foo@bar.com")
.assignee("bar@bax.com")
.subject("subj");
Run Code Online (Sandbox Code Playgroud)
相反,制作一个单独的可变构建器对象有什么好处吗?
更新2: 在阅读了apangin的答案后,我的基准无效.我会把它留在这里以供参考,如何不进行基准测试:)
更新: 我冒昧地用这段代码自己测量:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
如果字符串参数不是新对象,那么我发现差异很大(构建器更快),但所有相同(请参阅getString中的注释代码)
我认为这是一个更现实的场景,当所有字符串都是新对象时,差异可以忽略不计,JVM启动会导致第一个字符串变慢(我尝试了两种方式).
使用"new String"代码总共慢了许多倍(我不得不减少n),或许表明正在进行"新消息"的某些优化,而不是"新字符串".
apa*_*gin 12
是的,HotSpot JIT可以消除本地环境中的冗余分配.
此优化由JDK 6u23启用的Escape Analysis提供.它经常与堆栈分配混淆,但事实上它更强大,因为它不仅允许在堆栈上分配对象,而且通过用变量(标量替换)替换对象字段来完全消除分配.优化.
优化由-XX:+EliminateAllocationsJVM选项控制,默认情况下为ON.
由于分配消除优化,您创建Message对象的示例都以相同的方式有效地工作.他们不分配中间对象; 只是最后一个.
您的基准测试显示误导性结果,因为它收集了许多微基准测试的常见缺陷:
让我们用JMH正确测量它.作为奖励,JMH具有分配profiler(-prof gc),它显示每次迭代实际分配了多少字节.我添加了第三个EliminateAllocations禁用优化的测试,以显示差异.
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";
}
}
Run Code Online (Sandbox Code Playgroud)
结果如下.两者builder并且immutable执行相同并且每次迭代仅分配40个字节(恰好是一个Message对象的大小).
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
676 次 |
| 最近记录: |