【问题标题】:Is there a performance impact when using Guava.Preconditions with concatenated strings?将 Guava.Preconditions 与串联字符串一起使用时是否会对性能产生影响?
【发布时间】:2015-10-14 07:14:21
【问题描述】:

在我们的代码中,我们经常使用Preconditions检查参数:

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

但有时,此代码会被非常频繁地调用。这会对性能产生显着的负面影响吗?我们应该切换到

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

?

(我希望大多数情况下条件为真。假意味着错误。)

【问题讨论】:

  • 第一个真的只是简单的字符串字面连接?
  • 是的 - 这就是问题的核心 - 具有显着的性能影响
  • 字符串连接可能发生在编译时,因此它不应该影响运行时性能。也就是说,if 您正在连接文字(而不是变量)。
  • 变量字符串通过StringBuilder at runtime连接,所有常量部分在编译时放在一起。 .format() 保证具有最差的性能(必须弄清楚那些 %s 的含义等)。 (循环中除外)在源代码中使用+ 并让编译器弄清楚。
  • 1) 鉴于 Google Java 库团队必须维护他们添加的任何方法,添加不必要的方法对他们来说将是净损失。 2) 可变参数方法不需要提供带有占位符的消息,您可以简单地调用String.format,并避免添加重载。该方法的存在肯定表明 Google 的代码有一些净积极的好处;我没有数据可以说明这是否会带来性能优势。

标签: java performance guava preconditions


【解决方案1】:

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

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

值得一提的是,Guava 使用了一个自定义的更快的格式化程序,它只接受%s。所以损失时间其实更类似于标准的logger{}句柄(在slf4j或者log4j 2中)。但是如上所述,这是在您已经处于慢速分支的情况下。

无论如何,我强烈反对你的任何建议,但我会改用这个:

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

您应该只在%s 中放置变量,而不是常量来获得边际速度。

【讨论】:

    【解决方案2】:

    String 文字串联的情况下,编译器应该在编译时执行此操作,因此不会发生运行时性能损失。至少标准 JDK 会这样做,它不是按照规范(所以一些编译器可能不会优化这个)。

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

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

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

    【讨论】:

    • 谢谢,但我必须编辑我的问题 - 现在使用变量
    • 注意格式化程序只会在校验失败的时候被调用,这里不应该是正常情况。
    • 哦,是的,我错过了。在这种情况下,您可以使用其中任何一个。在答案中添加了我的想法。
    • 我的想法是,出于性能原因提供了格式化程序方法(如果条件错误,则“没有”发生)。 @meskobalazs“较新的...编译器”是什么意思。
    • 好吧,仍然支持任何东西 :) 尽管即使 Java 6 也应该这样做(也许 5 也是)。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-19
      • 2010-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多