2015-10-14 2 views
3

В нашем коде, мы часто проверить аргументы Preconditions:Возникает ли влияние производительности при использовании Guava. Предпосылки с конкатенированными строками?

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

Но иногда этот код называется очень часто. Может ли это оказать заметное негативное влияние на производительность? Должны ли мы переключаться на

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

?

(я ожидать, что условие истинной большой части времени. Ложное средство ошибки.)

+0

Первый действительно только простой строковый литерал конкатенации? – meskobalazs

+0

Да - и это ядро ​​вопроса - имеет это заметное влияние на производительность. – dermoritz

+0

Конкатенация строк, вероятно, происходит во время компиляции, поэтому она не должна влиять на производительность выполнения. То есть, если вы конкатенируете литералы (а не переменные). –

ответ

1

В случае String буквальной конкатенации, компилятор должен делать это время компиляции, так что не производительность во время выполнения удара не произойдет. По крайней мере, стандартный JDK делает это, это не по спецификации (поэтому некоторые компиляторы могут не оптимизировать это).

В случае переменных постоянное складывание не будет работать, поэтому во время выполнения будет работать. Однако новые компиляторы Java заменит конкатенацию строк на StringBuilder, что должно быть более эффективным, поскольку оно не является неизменным, в отличие от String.

Это должно быть быстрее, чем использование форматирования, если оно вызывается. Однако, если вы не хотите, чтобы это вызывалось очень часто, это может быть медленнее, поскольку конкатенация всегда происходит, даже если аргумент истинен, и метод ничего не делает.

В любом случае, чтобы его обернуть: я не думаю, что стоит переписать существующие вызовы. Однако, в новом коде, вы можете использовать форматировщик без сомнений.

+0

спасибо, но мне пришлось отредактировать свой вопрос - с помощью переменной теперь – dermoritz

+0

okay, отредактировал мой ответ – meskobalazs

+0

Обратите внимание, что форматирование будет вызываться только тогда, когда проверка завершится неудачно, что не должно это обычный случай. –

5

Если вы ожидаете, что проверка не будет вызывать какое-либо исключение большую часть времени, нет причин использовать конкатенацию строк. Вы потеряете больше времени, конкатенируя (используя .concat или StringBuilder), прежде чем вызывать метод, чем делать это после того, как убедитесь, что выбрали исключение.

С другой стороны, если вы выбрасываете исключение, вы уже находитесь в ветке slow.

Также следует отметить, что Guava использует настраиваемый и быстрый форматтер, который принимает только %s. Таким образом, потеря времени на самом деле больше похожа на стандартный регистратор {} handle (в 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); 
    } 

} 
Смежные вопросы