Java 使用连接字符串的Guava.Premissions时是否会影响性能?
在我们的代码中,我们经常使用Java 使用连接字符串的Guava.Premissions时是否会影响性能?,java,performance,guava,preconditions,Java,Performance,Guava,Preconditions,在我们的代码中,我们经常使用先决条件检查参数: Preconditions.checkArgument(expression, "1" + var + "3"); 但有时,这段代码经常被调用。这是否会对绩效产生显著的负面影响?我们应该换成电视吗 Preconditions.checkArgument(expression, "%s%s%s", 1, var, 3); ? (我希望大多数情况下条件为true。False意味着bug。)在字符串文本连接的情况下,编译器应该在编译时执行此操作,因此
先决条件检查参数
:
Preconditions.checkArgument(expression, "1" + var + "3");
但有时,这段代码经常被调用。这是否会对绩效产生显著的负面影响?我们应该换成电视吗
Preconditions.checkArgument(expression, "%s%s%s", 1, var, 3);
?
(我希望大多数情况下条件为true。False意味着bug。)在
字符串
文本连接的情况下,编译器应该在编译时执行此操作,因此不会出现运行时性能问题。至少标准JDK做到了这一点,它不是按照规范进行的(因此一些编译器可能不会对此进行优化)
对于变量,常量折叠不起作用,因此在运行时会有工作。但是,较新的Java编译器将取代字符串连接到StringBuilder
,这应该更有效,因为它不是不变的,不像string
这应该比使用格式化程序(如果调用的话)更快。但是,如果您不经常调用它,那么这可能会比较慢,因为连接总是发生,即使参数为true,并且方法什么也不做
总之,总而言之:我认为重写现有的调用是不值得的。但是,在新代码中,您可以毫无疑问地使用格式化程序。如果您希望检查在大多数情况下不会引发任何异常,则没有理由使用字符串串联。在调用方法之前连接(使用
.concat
或StringBuilder
)比在确定抛出异常后连接要浪费更多的时间
相反,如果抛出异常,则已经处于慢速分支中
还值得一提的是,Guava使用了一个定制的更快的格式化程序,它只接受%s
。因此,时间损失实际上更类似于标准记录器{}
句柄(在slf4j或log4j2中)。但是如上所述,这是在你已经在慢分支的情况下
在任何情况下,我都强烈反对你的任何建议,但我会使用以下建议:
Preconditions.checkArgument(expression, "1%s3", var);
您应该只在
%s
中放入变量,而不是常数以获得边际速度。我编写了一个简单的测试。正如这里所建议的,使用格式化程序要快得多。性能上的差异随着调用次数的增加而增加(formatter的性能不会改变O(1))。我猜在使用简单字符串的情况下,垃圾收集器的时间会随着调用次数的增加而增加
Here is one sample result:
started with 10000000 calls and 100 runs
formatter: 0.94 (mean per run)
string: 181.11 (mean per run)
Formatter is 192.67021 times faster. (this difference grows with number of calls)
下面是代码(Java8,Guava18):
import java.util.concurrent.TimeUnit;
导入java.util.function.Consumer;
导入com.google.common.base.premissions;
导入com.google.common.base.Stopwatch;
公共类应用程序{
公共静态void main(字符串[]args){
整数计数=10000000;
int运行=100;
System.out.println(“以“+count+”调用和“+runs+”运行开始”);
Stopwatch Stopwatch=Stopwatch.createStarted();
跑步(计数,跑步,i->fast(i));
秒表;
float fastTime=(float)秒表。已用时间(TimeUnit.ms)/运行;
System.out.println(“fast:+fastTime+”(每次运行的平均值)”;
//
秒表复位();
System.out.println(“重置:+秒表时间(时间单位毫秒));
秒表。开始();
运行(计数,运行,i->慢速(i));
秒表;
float-slowTime=(float)秒表。已用时间(TimeUnit.ms)/运行;
System.out.println(“慢:+slowTime+“(每次运行的平均值)”);
浮动时间=慢时间/快时间;
System.out.println(“格式化程序的速度是“+倍+”倍。”);
}
私有静态无效运行(整数计数、整数运行、使用者函数){
对于(int c=0;c)来说,第一个实际上只是一个简单的字符串-文字连接?是的,这是问题的核心-这对性能有显著影响字符串连接可能发生在编译时,因此它不应该影响运行时性能。也就是说,如果您连接的是文字(而不是变量).变量字符串通过连接,所有常量部分在编译时放在一起。.format()
保证具有最差的性能(必须找出那些%s的含义等)。(循环中除外)在源代码中使用+
,让编译器找出它。1)考虑到Google Java Libraries团队必须维护他们添加的任何方法,添加不必要的方法对他们来说将是一个净损失。2)变量方法不必提供带有占位符的消息,您只需调用String.format
,而不必添加重载。必须确保该方法存在我要指出的是,谷歌的代码有一些净的积极的好处;我没有数据说明这是否是一个性能优势。谢谢,但我必须编辑我的问题-现在使用一个变量注意,格式化程序只会在检查失败时调用,这不应该是正常情况。哦,是的,我错过了。在这种情况下,你可以也可以使用。在回答中添加了我的想法。我的想法是,提供格式化程序方法是出于性能原因,因此(“如果条件不正确,则不会发生任何事情”)@meskobalazs“更新的……编译器”是什么意思。好吧,仍然支持的任何东西:)尽管Java 6也应该这样做(可能也是5)。
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
public class App {
public static void main(String[] args) {
int count = 10000000;
int runs = 100;
System.out.println("started with " + count + " calls and " + runs + "runs");
Stopwatch stopwatch = Stopwatch.createStarted();
run(count, runs, i->fast(i));
stopwatch.stop();
float fastTime = (float)stopwatch.elapsed(TimeUnit.MILLISECONDS)/ runs;
System.out.println("fast: " + fastTime + " (mean per run)");
//
stopwatch.reset();
System.out.println("reseted: "+stopwatch.elapsed(TimeUnit.MILLISECONDS));
stopwatch.start();
run(count, runs, i->slow(i));
stopwatch.stop();
float slowTime = (float)stopwatch.elapsed(TimeUnit.MILLISECONDS)/ runs;
System.out.println("slow: " + slowTime + " (mean per run)");
float times = slowTime/fastTime;
System.out.println("Formatter is " + times + " times faster." );
}
private static void run(int count, int runs, Consumer<Integer> function) {
for(int c=0;c<count;c++){
for(int r=0;r<runs;r++){
function.accept(r);
}
}
}
private static void slow(int i) {
Preconditions.checkArgument(true, "var was " + i);
}
private static void fast(int i) {
Preconditions.checkArgument(true, "var was %s", i);
}
}