6

使用 JProfiler,我在我的 Java 代码中发现了一个我无法理解的热点。JProfiler 解释说,这种方法平均需要 150μs(没有预热的情况下为 674μs),不包括调用后代方法所需的时间。150μs 可能看起来不多,但在这个应用程序中,它加起来(并且我的用户体验过)而且看起来很多,与其他对我来说似乎比这个更复杂的方法相比。因此,这对我很重要。

private boolean assertReadAuthorizationForFields(Object entity, Object[] state,
        String[] propertyNames) {
    boolean changed = false;
    final List<Field> fields = FieldUtil.getAppropriatePropertyFields(entity, propertyNames);
    // average of 14 fields to iterate over
    for (final Field field : fields) {
        // manager.getAuthorization returns an enum type
        // manager is a field referencing another component
        if (manager.getAuthorization(READ, field).isDenied()) {
            FieldUtil.resetField(field.getName(), state, propertyNames);
            changed = true;
        }
    }
    return changed;
}

我自己在不同的方向上最小化了这种方法,但它从来没有教给我太多有用的东西。我不能过分强调 JProfiler 报告的持续时间 (150μs) 仅与此方法中的代码有关,不包括执行getAuthorizationisDenied等所需的时间resetField。这也是为什么我首先发布这个片段,没有太多上下文,因为问题似乎出在这段代码上,而不是它随后的后代方法调用。

也许你可以争论为什么——如果你觉得我看到了鬼魂:) 不管怎样,谢谢你的时间!

4

3 回答 3

1

可能会减慢您速度的候选人行为:

  • 主要效果:明显迭代。如果你有很多领域......你说平均14,这是相当重要的
  • 主要影响:热点内联意味着调用的方法包含在您的时间中 - 这可能很明显,因为您的方法调用使用反射。getAppropriatePropertyFields 对类字段定义元数据进行自省;resetField 动态调用 setter 方法(可能使用 Method.invoke()??)。如果你对性能非常渴望,你可以通过 HashSet 使用缓存(映射 ElementClass->FieldMetadataAndMethodHandle) 这可以包含字段元数据和 setter 方法的 MethodHandles(而不是使用 method.invoke,这很慢)。然后,您将只在应用程序启动期间进行反映,并使用 JVM 的快速 dynamicInvoke 支持。
  • 次要影响 - 但乘以迭代次数:如果您有非常大的数组用于状态和属性名称,并且它们使用原始字段,那么它们将在方法调用期间涉及某种程度的复制(方法参数实际上是按“值”传递的)表示按引用传递/按原语复制传递)
于 2013-04-21T14:40:57.123 回答
0

我建议您自己为该方法计时,因为分析器并不总是给出准确的时间。

仅使用此代码创建一个微基准,并将其计时至少 2 秒。要计算出方法调用有多大的不同,请将它们注释掉并对它们返回的值进行硬编码。

于 2013-03-17T04:37:11.063 回答
0

我认为问题在于 FieldUtil 正在使用反射并且不缓存它正在使用的字段。

于 2013-05-31T15:23:06.080 回答