java - 如果编译器可以内联日志调用,为什么还要在日志 API 中使用 lambda 表达式

标签 java logging lambda

许多日志记录框架(例如 log4j)允许您将 lambda 表达式而不是 String 传递给日志记录 API。论据是,如果字符串构造起来特别具有表现力,则可以通过 lambda 表达式延迟执行字符串构造。这样,仅当系统的日志级别与调用的日志级别匹配时才构建字符串。

但是,鉴于现代编译器会自动执行很多方法内联,以这种方式使用 lambda 表达式真的有意义吗?我将在下面提供一个简化的示例来证明这种担忧。

假设我们传统的日志记录方法是这样的:

void log(int level, String message) {
    if (level >= System.logLevel)
        System.out.println(message);
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, "Very expensive string to construct ..." + etc);

假设 FINE 小于 CRITICAL,因此,虽然构造了一个昂贵的字符串,但由于没有输出消息,所以这一切都是为了 not。

Lambda 日志记录 API 有助于解决这种情况,因此仅在必要时才评估(构造)字符串:

void log(int level, Supplier<String> message) {
    if (level >= System.logLevel)
        System.out.println(message.apply());
}
// ....
System.logLevel = Level.CRITICAL;
log(Level.FINE, () -> "Very expensive string to construct ..." + etc);

但是,编译器可以直接内联日志记录方法,这样最终效果如下:

System.logLevel = Level.CRITICAL;
if (Level.FINE >= System.logLevel)
    System.out.println("Very expensive string to construct..." + etc);

在这种情况下,我们不必在调用日志记录 API 之前评估字符串(因为没有),而且据推测,我们可以仅从内联中获得性能。

总而言之,我的问题是,鉴于编译器可能会内联日志记录 API 调用,lambda 表达式如何在这种情况下帮助我们?我唯一能想到的是,不知何故,在 lambda 情况下,如果日志记录级别不匹配,则不会评估字符串。

最佳答案

您的优化不仅引入了内联,还改变了顺序。这通常无效。

特别是,改变是否调用方法是无效的,除非 JIT 可以证明那些方法没有其他作用。如果 JIT 编译器将内联 和重新排序 到那种程度,我会感到非常惊讶——检查构造方法参数所涉及的所有操作没有副作用的成本可能不值得大多数情况下受益。 (JIT 编译器无法将日志记录方法与其他方法区别对待。)

因此,虽然非常、非常聪明的 JIT 编译器可能做到这一点,但看到任何实际上做到这一点,我会感到非常惊讶。如果您发现自己正在使用一种方法,并编写测试来证明这种方法并不比使用 lambda 表达式更昂贵,并且随着时间的推移继续证明这一点,那就太好了 - 但听起来您更热衷于假设是这种情况,我绝对不会。

关于java - 如果编译器可以内联日志调用,为什么还要在日志 API 中使用 lambda 表达式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47682777/

相关文章:

c# - Lambda 或 LINQ 按 Shift 对列表 <Employee> 进行排序,C#

lambda - 是否可以在不使用 lambda 的情况下将本地过程绑定(bind)到 letrec 中的变量?

java - 在测试中找不到保存的实体

java - 优先级队列方法通过简单的递减返回最小值

java - 具有过期可能性的简单 Java 字符串缓存

mysql - 仅记录插入、更新和删除 mysql 查询

java - 获取系统日志的完整输出(终端)

ios - 隐藏奇怪的不需要的 Xcode 日志

c# - Linq 表达式如何确定相等性?

Java 按唯一索引号对值进行排序