2015-10-14 33 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); 
    } 

} 
相關問題