2015-10-14 67 views
3

在我们的代码中,我们经常检查论点Preconditions使用Guava.Preconditions与连接字符串时是否会影响性能?

Preconditions.checkArgument(expression, "1" + var + "3"); 

但有时,这种代码被称为非常频繁。这会对性能产生显着的负面影响吗?我们是否应该切换到

Preconditions.checkArgument(expression, "%s%s%s", 1, var, 3); 

(我期待状况的真实的大部分时间。造假手段的错误。)

+0

第一个是真的只是一个简单的字符串字面串联? – meskobalazs

+0

是的 - 这就是问题的核心 - 有这种显着的性能影响 – dermoritz

+0

字符串连接可能发生在编译时,所以它不应该影响运行时性能。也就是说,_if_你连接文字(不是变量)。 –

回答

1

String字面串联的情况下,编译器应该在编译时间做到这一点,所以不会出现运行时性能损失。至少标准的JDK是这样做的,它不是每个规范(所以一些编译器可能不优化这个)。

在变量的情况下,常量折叠不起作用,所以在运行时会有工作。但是,较新的Java编译器会将字符串连接替换为StringBuilder,这应该更有效率,因为它不是不可变的,与String不同。

这应该比使用格式化程序更快,如果它被调用。但是,如果你不需要经常调用它,那么这可能会比较慢,因为连接总是会发生的,即使参数是真实的,并且该方法什么也不做。

无论如何,总结:我认为不值得重写现有的调用。但是,在新代码中,您可以毫无疑问地使用格式化程序。

+0

谢谢,但我不得不编辑我的问题 - 现在使用一个变量 – dermoritz

+0

好吧,编辑我的答案 – meskobalazs

+0

请注意格式化程序只会在检查失败时被调用,而不应该这里是正常的情况。 –

5

如果您希望检查在大多数情况下不会抛出任何异常,则没有理由使用字符串连接。调用方法之前,你会失去更多的时间连接(使用.concatStringBuilder),而不是在确定抛出异常之后进行连接。

反过来,如果你抛出异常,你已经在缓慢分支。

值得一提的是,Guava使用的定制和快速格式化程序只接受%s。所以时间的损失实际上更类似于标准记录器{}句柄(在slf4j或log4j 2中)。但正如上面所写,这是你已经在慢分支中的情况。

在任何情况下,我会强烈反对任何您的建议,但我会用这一个来代替:

Preconditions.checkArgument(expression, "1%s3", var); 

您应该只把变量%s,不是常量获得的边际速度。

0

我写了一个简单的测试。如此处所示,使用格式化程序要快得多。性能的差异会随着调用次数的增加而增加(格式化程序的性能不会改变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) 

下面是代码(Java的8,番石榴18):

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); 
    } 

} 
相关问题