我正在尝试降低日志记录语句的性能/垃圾收集成本。我希望有很多日志语句可以用于调试,但也有一种方法可以将它们关闭以进行快速生产。

我运行了一个关于调用以下方法的基准测试:

public static final isLogging = false;

public static logObjs(Object[] params) {
  if(isLogging)
    System.out.println(params[0]);
}

public static log3Obj(Object a, Object b, Object c) {
  if(isLogging)
     System.out.println(a);
}

public static logInts(int a, int b, int c) {
  if(isLogging)
    System.out.println(a);
}

我使用驱动程序方法对功能进行了基准测试
long sum = 0;
for(int i = 0; i < 100000000; ++i) {
   int a = i; int b = i+1; int c = i+2;
   logFoo(a,b,c);
   sum += a; }

logObjs(i, i+1, i+2) 进行 1e8 次迭代大约需要 2 秒并产生大量垃圾。我假设,来源是整数的自动装箱和 Object[] 为变量 # of parameters 创建。

log3Obj 产生了很多(虽然更少)垃圾,大约需要 1.2 秒;再次,我假设自动装箱仍然发生。

logInts 非常快(0.2 秒),与没有函数调用的循环一样快。

因此,问题是即使该函数确定性地不执行任何操作,自动装箱仍然会发生。在我的代码中,我实际上更希望 isLogging 不是最终的,而是让它成为运行时参数,但为了做到这一点,这个更简单的情况(编译器可以证明该函数不执行任何操作)应该运行。当然,我可以用
if(isLogging)
   logObjs(a, b, c);

但这很不雅观。我认为这是 JIT 应该处理的事情。我已经尝试了一堆编译器设置,但也许我遗漏了什么?如何让代码在什么都不做的情况下不产生这么多垃圾?

最佳答案

这与这个问题非常相似:Will the Java optimizer remove parameter construction for empty method calls?

正如我在那里写的那样:JIT 很可能会意识到它不必做任何事情(在您的情况下会少一点,因为它涉及更多)。但显然它似乎并没有这样做。

我建议创建多个日志方法,一个带有一些可变参数的通用方法和一些采用整数并首先防止自动装箱的重载方法:

Log(Object... arguments) { /* do logging */ }
Log(Object a, Object b, Object c}  { /* special case for 3 objects */ }
Log(int a, int b, int c}  { /* special case for 3 ints */ }

更新:更好的是,请参阅 Péter Török '不要重新发明轮子' 的答案......

关于java - 调用什么都不做的函数的开销(速度和垃圾),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/3095150/

10-12 00:12
查看更多