【问题标题】:Should I cache System.getProperty("line.separator")?我应该缓存 System.getProperty("line.separator") 吗?
【发布时间】:2017-12-08 08:25:45
【问题描述】:

考虑这样的方法:

@Override
public String toString()
{
    final StringBuilder sb = new StringBuilder();
    for (final Room room : map)
    {
        sb.append(room.toString());
        sb.append(System.getProperty("line.separator")); // THIS IS IMPORTANT
    }
    return sb.toString();
}

System.getProperty("line.separator") 可以多次调用。

我应该用public final static String lineSeperator = System.getProperty("line.separator") 缓存这个值吗? 以后只使用lineSeperator

或者System.getProperty("line.separator") 和使用静态字段一样快?

【问题讨论】:

  • 我认为 System.getProperty 应该在每次调用时查找指定的键。我不知道编译器是否对此进行了任何优化。让我们看看人们的建议。
  • 您使用的是 Java 7 还是旧版本?
  • @chrylis 我正在使用 6,我正在考虑从 7 开始(到目前为止,我没有看到升级的充分理由......)。这个getProperty() 6 和 7 有什么区别吗?
  • 是的,7 将此特定问题推广到 System.lineSeparator()。可能不值得升级,但如果您已经在使用它,这是一个更简单的答案。

标签: java caching optimization


【解决方案1】:

我认为您的问题提出了错误的二分法。我既不会每次都打电话给getProperty,也不会为它声明一个静态字段。我只需将其提取到toString 中的局部变量中。

@Override
public String toString()
{
    final StringBuilder sb = new StringBuilder();
    final String newline = System.getProperty("line.separator"); 
    for (final Room room : map) sb.append(room.toString()).append(newline);
    return sb.toString();
}

顺便说一句,我已经对通话进行了基准测试。代码:

public class GetProperty
{
  static char[] ary = new char[1];
  @GenerateMicroBenchmark public void everyTime() {
    for (int i = 0; i < 100_000; i++) ary[0] = System.getProperty("line.separator").charAt(0);
  }
  @GenerateMicroBenchmark public void cache() {
    final char c = System.getProperty("line.separator").charAt(0);
    for (int i = 0; i < 100_000; i++) ary[0] = (char)(c | ary[0]);
  }
}

结果:

Benchmark                     Mode Thr    Cnt  Sec         Mean   Mean error    Units
GetProperty.cache            thrpt   1      3    5       10.318        0.223 ops/msec
GetProperty.everyTime        thrpt   1      3    5        0.055        0.000 ops/msec

缓存的方法要快两个数量级以上。

请注意,getProperty 调用对所有字符串构建的总体影响非常非常不可能被注意到。

【讨论】:

  • 不是一个好答案,如果缓存更快你应该缓存到静态,如果你多次调用toString怎么办?
  • 系统属性可以在调用之间改变。特别是,用户可能只设置一次,但是在这个类已经被初始化之后。另一方面,仅当映射中的项目数量非常少时,静态缓存才会在每个toString 调用中赢得一次访问。您不能通过提及与此选项具有不同权衡的替代方案来声称这不是“一个好的答案”。
  • 为什么 System.getProperty("line.separator") 会如此频繁地更改?如果行分隔符正在改变 inbween 调用,那么任何 toStringiness 肯定是无法理解的。
【解决方案2】:

您不必担心代码运行时行分隔符会发生变化,因此我认为没有理由反对缓存它。

缓存一个值肯定比一遍又一遍地执行调用要快,但差异可能可以忽略不计。

【讨论】:

  • 看热点优化时是否内联调用:)
  • 哦,对了,好点子。但我个人不喜欢依赖这些功能 - 拥有已经看起来高效的代码不是更好吗?
  • 我完全同意!开发人员经常以将在运行时修复的借口挥霍诚实的低效率。只是提出一个观点。
  • @WilliamMorrison 调用的内联绝对不会产生影响,因为大部分时间都花在getProperty 方法中。如果您的意思是 JIT 可能会内联从该方法返回的 value,我会说这是极不可能的,我的基准测试证实了 OpenJDK 7 的情况。
  • 我的意思是后者。是的,我看到了你的基准,干得好。我一定是弄错了。
【解决方案3】:

如果您发现与此相关的性能问题,是的。

如果你没有,那么不,查找不太可能有足够的开销。

这属于一般类别“微优化”和“过早优化”中的一个或两个。 :-)


但是,如果您担心效率,您可能有一个 更大的机会,因为您的 toString 方法每次都会重新生成字符串。如果toString 会被大量调用,而不是缓存行终止符,缓存生成的字符串,并在房间地图发生变化时清除它。例如:

@Override
public String toString()
{
    if (cachedString == null)
    {
        final StringBuilder sb = new StringBuilder();
        final String ls = System.getProperty("line.separator");
        for (final Room room : map)
        {
            sb.append(room.toString());
            sb.append(ls);
        }
        cachedString = sb.toString();
    }
    return cachedString;
}

...当你的地图改变时,做

cachedString = null;

这是一个很多更多的降压(降压是额外字段的开销)。当然,它是针对每个实例而不是针对每个类的,因此(参考之前关于效率的评论)只有在您有充分理由的情况下才这样做。

【讨论】:

  • 过早的优化可能会适得其反,因为它会导致“糟糕”的代码。在这种情况下,代码会更快并且更易读(如果不是更多的话),所以我认为它不属于该类别......
  • @assylias:首先,让我说:很少有人在过早优化方面比我更糟糕。 :-) 但是 PO 不是(仅)关于创建糟糕的代码。它是关于花费时间、精力和内存来优化不需要优化的东西,而您可以使用那些时间、精力和内存来优化确实需要优化的东西或添加功能。
  • @MarkoTopolnik:在函数中使用本地没有问题。不过,OP 谈论使用 static,请参阅 cmets 给 Alex MDC。
  • 这是最好的答案
【解决方案4】:

既然做起来很容易,为什么不呢? 至少System.getProperty() 的实现必须进行哈希表查找(即使在内部缓存)以找到您请求的属性,然后将调用虚拟方法 getString()结果对象。这些都不是很昂贵,但需要多次调用。更不用说会创建许多String 临时对象并且之后需要GCing。

如果将它移到循环顶部并重复使用相同的值,则可以避免所有这些问题。那为什么不呢?

【讨论】:

  • “既然做起来这么容易,何乐而不为呢?” 因为如果没关系,创建额外的静态字段是个坏主意。每个使用任何不会更改的系统属性的类都应该保留它自己的副本吗?还有其他不变的东西?当没有明显的性能提升时?不会。这只会导致内存中充斥着冗余信息。
  • 我同意,我的意思是在进入循环之前在函数内部计算一次。我没有注意到 OP 正在使用静态值的建议。
  • @alex:啊,是的,那不一样。 :-)
【解决方案5】:

如果系统属性保证在应用程序期间保持不变,它可以被缓存,但通常你会失去属性的特性,当你改变它时,它会改变行为。

例如,文本生成器可以使用该属性为 windows 或 linux 生成文本,并允许在应用程序中动态更改该属性,为什么不呢?

一般来说,捕获属性意味着使函数 setProperty 无用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-31
    • 1970-01-01
    • 2023-03-03
    • 2016-08-16
    • 2017-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多