【问题标题】:Why is accessing instanced fields in static context faster than instanced context in JAVA为什么在静态上下文中访问实例化字段比在 JAVA 中访问实例化上下文更快
【发布时间】:2020-10-04 12:20:05
【问题描述】:

我一直认为从实例化方法/函数访问实例化字段应该会提高性能,因为数据应该“更本地可用”(我知道这可能取决于系统和 JVM),而不是从静态上下文。请参阅说明我的观点的代码:

public class Lecture
{
    public static void main(String[] args)
    {
        hold h1 = new hold();   
        long start1 = System.currentTimeMillis();
            h1.accessOOPs();
        long end1 = System.currentTimeMillis();
        System.out.println("OOPS: "+ (end1 - start1));

        hold h2 = new hold();   
        long start2 = System.currentTimeMillis();
            hold.accessStatic(h2);
        long end2 = System.currentTimeMillis();
        System.out.println("Static (same class): "+ (end2 - start2));

        hold h3 = new hold();   
        long start3 = System.currentTimeMillis();
            accessStatic(h3);
        long end3 = System.currentTimeMillis();
        System.out.println("Static (different class): "+ (end3 - start3));
    }
    public static void accessStatic(hold h)
    {
        for (int i=0;i<h.vars.length;i++)
            h.vars[i] = i;
        for (int i : h.vars)
            h.var1 += i;
        for (int i: h.vars)
            h.name += i;
    }
}
class hold
{
    int var1;
    int vars[] = new int[10000];
    String name;
    public void accessOOPs()
    {
        for (int i=0;i<vars.length;i++)
            vars[i] = i;
        for (int i : vars)
            var1 += i;
        for (int i: vars)
            name += i;
    }
    public static void accessStatic(hold h)
    {
        for (int i=0;i<h.vars.length;i++)
            h.vars[i] = i;
        for (int i : h.vars)
            h.var1 += i;
        for (int i: h.vars)
            h.name += i;
    }
}

在代码中,我有 3 个定时示例,我在其中访问/修改简单对象中的属性。第一个示例调用对象中的实例方法,因此理论上所有属性都应该更快地访问,因为它们与方法处于相同的上下文中。第二种调用对象类中的静态函数,每次都使用点运算符来访问属性。我的假设是这会更慢。第三个示例重复与第二个示例相同的操作,但这次是在一个单独的类中进行。我对收到的时间感到很困惑:

示例运行 1:

  • 哎呀:135
  • 静态(同一类):130
  • 静态(不同类):120

示例运行 2:

  • 哎呀:137
  • 程序(同一类):135
  • 程序(不同类别):128

当方法在单独的类中时,OOPs 方法始终优于静态方法,但我不明白为什么当静态方法在同一个类中时它会优于实例方法。这只是一个微小的差距,但它非常一致。这仅仅是因为静态实现正在缓存对正在访问的对象的引用吗?如果发生这种情况,那么我认为静态方法是否在单独的类中并不重要,所以我完全糊涂了。谁能回答为什么静态方法没有明显变慢?

【问题讨论】:

标签: java static instance-variables


【解决方案1】:

您的 h3 变量未传递给任何调用,因此在第三次调用中对 h2 进行的计算与之前对 h1/h2 的计算不同。

在进行时序测试时,您应该只测量预热的代码(因为 JIT 编译器会稍微改变性能)。如果你修复使用 h3 并在循环中运行 main 的代码,你会发现运行之间几乎没有区别,我得到的最后一次运行是

OOPS: 56
Static (same class): 56
Static (different class): 50

您可以看到 accessOOPs 和 accessStatic 的字节码结构非常相似:

javap -v hold.class > hold.log
javap -v Lecture.class > lecture.log

在 JDK14 中,它们都是 114 项,仅字段/索引位置不同,WINDIFF.EXE 中显示了差异

【讨论】:

  • 谢谢,h3 只是在剔除示例时打错了。但是我仍然倾向于从实例化示例中获得稍慢的性能,或者如果没有其他非常相似的结果。实例化方法不应该有显着的性能提升吗,因为它访问属性不需要内存控制器,而静态示例必须使用内存控制器访问对象 - 或者 JAVA 不能那样工作 - 或者是这样只是缓存对象的一个​​例子?
  • 除非你在 h1-2-3 上循环运行 main 超过 2-3 次,否则你不会排除 h1/2 弄乱 h3 的影响,因为你的字符串处理。也尝试 h3-2-1 订单。但是只运行一次,比较时间是困难的,因为你的步骤“h.name += i”对内存有很大的影响。 (如果你不相信我注释掉所有引用 h.name 的行)。
  • 当我对其进行更多测试时,我确实删除了名称并仅使用一些简单的数据类型对其进行了测试——是的,我需要更多的迭代来获得计时。我还以不同的顺序测试了 h1-2-3 并得到了相似的结果(接近时间或实例化方法的速度稍慢)。我应该提一下,我有一个 h0 在执行预热阶段的所有操作之前调用每个方法(可能并不完美,但它可以阻止第一次运行花费太多额外时间)。在我看来,实例方​​法应该比静态方法获得更好的性能,但事实并非如此。
  • 当不考虑继承和覆盖的好处时,您声明的带有对象参数的静态方法与带有隐式“this”参数的实例方法并没有真正的不同。我不希望性能会有所不同。
  • 我以为 this.x,这里是 .是一个范围解析运算符,而不是 instance.x,这里的 .应该是内存控制器。只是java对两者使用相同的符号。并且在 C/C++ 中,至少内存控制器比范围解析运算符要昂贵得多,因为范围解析运算符只需要通过对函数指针进行一些算术运算来偏移数据成员,因为我们处于对象内存上下文中已经。还是 java 真的有那么不同且未优化(尽管我几乎不敢相信),但这是我问题的核心?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-19
  • 2019-12-26
  • 1970-01-01
  • 2021-07-11
  • 1970-01-01
相关资源
最近更新 更多