调用函数的开销(速度和垃圾)什么都不做

时间:2010-06-22 16:34:02

标签: java garbage-collection jit

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

我在调用以下方法时运行了基准测试:

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; }
对于1e8次迭代,logObjs(i,i + 1,i + 2)大约需要2秒,并产生大量垃圾。我认为,这些来源是整数的自动装箱和变量#of parameters的Object []创建。

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

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

因此,问题是即使该功能确定性地没有做任何事情,自动装箱仍然会发生。在我的代码中,我实际上更喜欢让isLogging不是final,而是让它成为运行时参数,但为了做到这一点,这个更简单的情况(编译器可以证明函数不做任何事情)应该跑。当然,我可以用

替换所有的日志记录语句
if(isLogging)
   logObjs(a, b, c);

但这非常不优雅。我认为这是JIT应该照顾的事情。我尝试了一堆编译器设置,但也许有些东西我不见了?如何使代码在什么都不做的情况下不会产生太多垃圾?

3 个答案:

答案 0 :(得分:4)

你应该更喜欢像Log4J这样的现有日志框架而不是重新发明轮子。那些家伙已经投入了更多精力来优化他们的日志框架,而不是你现实中所拥有的。请参阅此Short introduction to log4j末尾附近的“效果”部分。

此外,不要试图过早优化。有可能,正在努力优化日志记录语句(或者就此而言,无论你的应用程序的哪一部分)没有实际测量,并证明这实际上改善了事情不会有回报。

答案 1 :(得分:1)

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

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

我建议的是制作多个日志方法,一般是带有一些vararg参数的方法,一些带有整数并且首先防止自动装箱的重载方法:

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éterTörök'不要重新发明轮子的答案......

答案 2 :(得分:1)

我认为你应该记录你需要记录的内容,而不是将整数转换为对象。

调用logObjs()或log3Obj()来记录整数只是浪费时间 - 您将为装箱/拆箱创建临时对象,并且需要处理它们。你无能为力。

我怀疑大多数情况下,当你打算调用logObjs()时,你会将实际对象传递给函数,在这种情况下,GC成本几乎为零,因为你只需要传递对这些对象的引用到logObjs()并且不需要创建或处理对象。

编写几个日志记录函数,这些函数采用与您当时要记录的内容相对应的不同参数列表。我就是做这个的。如果我需要记录字符串和32位值,我有一个带有这些参数的日志API等。没有(或很少)不必要/临时对象创建。

如果需要记录3个整数,请编写一个需要3个整数的API。如果你需要记录一个weeble对象和一个摆动对象和一个不倒下的对象,那么也要为它编写一个API。 通过使用适当的API来记录日志功能,最大限度地减少box / unobx / GC效果。