访问实例变量时使用关键字this是不是效率更高?

发布于 2024-10-20 09:44:35 字数 687 浏览 5 评论 0原文

在使用 C# 和 Java 等语言访问实例字段、属性和方法时,this 关键字是可选的。

我最近一直在对各种语言进行一些最佳实践研究,并且注意到很多地方建议在方法中创建对实例字段的本地引用,因为它更有效。最新的提及是在 Android 教程中。

在我看来,如果您指定 this._obj,它应该与局部变量一样有效。这是正确的还是与不使用 this 一样“昂贵”?

答案是否会从 Android Dalvik VM 变为标准 Java,再变为 C#?

public class test {
    private Object[] _obj;

    protected void myMethod() {
        Object[] obj = _obj;

        // Is there an appreciable differnce between
        for(int i = 0; i < obj.length; i++) { 
            // do stuff
        }

        // and this?
        for(int i = 0; i < this._obj.length; i++) { 
            // do stuff
        }
    }
}

The this keyword is optional when accessing instance fields, properties, and methods in languages like C# and Java.

I've been doing some best practice research on various languages lately, and have noticed many places recommend creating a local reference to instance fields within methods because it's more efficient. The latest mention was in an Android tutorial.

It seems to me that if you specify this._obj, it should be just as efficient as a local variable. Is this correct or is it just as 'costly' as not using this?

Does the answer change from the Android Dalvik VM, to standard Java, to C#?

public class test {
    private Object[] _obj;

    protected void myMethod() {
        Object[] obj = _obj;

        // Is there an appreciable differnce between
        for(int i = 0; i < obj.length; i++) { 
            // do stuff
        }

        // and this?
        for(int i = 0; i < this._obj.length; i++) { 
            // do stuff
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

小嗷兮 2024-10-27 09:44:35

至少对于标准 Java 来说,存在着非常非常小的差异。

我对您的示例进行了一些修改:

public class test {
    private Object[] _obj;

    protected void myMethodLocal() {
        Object[] obj = _obj;

        // Is there an appreciable differnce between
        for(int i = 0; i < obj.length; i++) { 
            // do stuff
        }
    }

   protected void myMethodMember() {
        // and this?
        for(int i = 0; i < this._obj.length; i++) { 
            // do stuff
        }
   }
}

因此 myMethodLocal() 会将 _obj 缓存到局部变量中,而 myMethodMember() 使用类成员<代码>_obj。

现在,让我们反编译它(使用 javap):

protected void myMethodLocal();
  Code:
   0:   aload_0
   1:   getfield    #2; //Field _obj:[Ljava/lang/Object;
   4:   astore_1
   5:   iconst_0
   6:   istore_2
   7:   iload_2
   8:   aload_1
   9:   arraylength
   10:  if_icmpge   19
   13:  iinc    2, 1
   16:  goto    7
   19:  return

protected void myMethodMember();
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   aload_0
   4:   getfield    #2; //Field _obj:[Ljava/lang/Object;
   7:   arraylength
   8:   if_icmpge   17
   11:  iinc    1, 1
   14:  goto    2
   17:  return

无需详细说明,后一个示例必须在每次循环迭代时访问 _obj 字段,而第一个示例已对其进行缓存在本地引用中,只需要访问本地引用。

这相当于什么速度差异?

不多。

虽然访问本地引用和类引用之间的区别在像 Python,对于 Java,你真的不需要担心。保持代码的可读性和可维护性比担心这样的细节要重要得多。

(另外,无论如何,上面的字节码没有考虑 JIT 编译器可能执行的操作)。

如果您通过函数获取实例字段,例如getObj(),我会将其插入到变量中,这样您就不需要继续调用getObj () 每次您想使用同一字段时。


另外,作为一个小注意事项,您可能应该将类命名为 Test 而不是 test。 Java 倾向于使用大驼峰式命名法来命名类名。

For at least standard Java, there is a small, small difference.

I modified your example a little to this:

public class test {
    private Object[] _obj;

    protected void myMethodLocal() {
        Object[] obj = _obj;

        // Is there an appreciable differnce between
        for(int i = 0; i < obj.length; i++) { 
            // do stuff
        }
    }

   protected void myMethodMember() {
        // and this?
        for(int i = 0; i < this._obj.length; i++) { 
            // do stuff
        }
   }
}

So myMethodLocal() will cache _obj into a local variable, while myMethodMember() uses the class member _obj.

Now, let's decompile this (using javap):

protected void myMethodLocal();
  Code:
   0:   aload_0
   1:   getfield    #2; //Field _obj:[Ljava/lang/Object;
   4:   astore_1
   5:   iconst_0
   6:   istore_2
   7:   iload_2
   8:   aload_1
   9:   arraylength
   10:  if_icmpge   19
   13:  iinc    2, 1
   16:  goto    7
   19:  return

protected void myMethodMember();
  Code:
   0:   iconst_0
   1:   istore_1
   2:   iload_1
   3:   aload_0
   4:   getfield    #2; //Field _obj:[Ljava/lang/Object;
   7:   arraylength
   8:   if_icmpge   17
   11:  iinc    1, 1
   14:  goto    2
   17:  return

Without going into details, the latter example has to access the _obj field every loop iteration, while the first example already had it cached in a local reference and just needs to access the local reference.

What does this equate to in speed difference?

Not much.

While the difference between accessing a local reference and a class-reference means a lot more in a language like Python, for Java, you really don't need to worry. It's much more important to keep your code readable and maintainable than to fret over details like that.

(Plus, the above bytecode doesn't take into account what the JIT compiler might do, anyway).

If you get the instance field by a function, like getObj(), I would plug that into a variable, so you don't need to keep calling getObj() each time you want to use the same field.


Also, just as a minor note, you should probably call your class Test instead of test. Java tends to favor Upper Camel Case for class names.

梦里°也失望 2024-10-27 09:44:35

不,效率绝对没有变化。请记住,在许多语言中,多个等效表达式将简化为底层字节码或汇编或任何高级语言翻译成的内容中的相同语句。

您提到的所有语言和虚拟机的答案都是统一的。

必要时使用它,例如方法参数与实例变量同名时。

除非 CPU 周期(或内存等)是重中之重,否则清晰度要高于表达力较差但更高效的语言语法。

No, there is absolutely no change in efficiency. Remember that in many languages, several equivalent expressions will reduce down to identical statements in the underlying bytecode or assembly or whatever the higher level language translates into.

The answer is uniform across the languages and VMs you mention.

Use it when necessary, like when a method parameter has the same name as an instance variable.

Unless CPU cycles (or memory, etc.) are a top priority, value clarity above less expressive but more efficient language syntax.

爱,才寂寞 2024-10-27 09:44:35

this 关键字用于提高可读性,最重要的是使变量名称明确。它对性能没有任何影响。

The this keyword is used for readability and most importantly making variable names unambiguous. It has no affect on performance whatsoever.

疯到世界奔溃 2024-10-27 09:44:35

在现代 PC 上,由于缓存,无论语言如何,这可能不会产生任何差异 - 如果内存位置缓存在片上染料上,那么它不会产生任何差异。

On modern PC, this may not make any difference irrespective of languages due to caching - if memory location is cached on on-chip dye then it wouldn't make any difference.

鹿港小镇 2024-10-27 09:44:35

我怀疑使用局部变量只是一个引用(值在堆栈上),而使用成员变量是2个引用(引用this,它在堆栈上,然后引用变量本身,位于堆上)

根据系统的不同,堆或堆栈访问可能会更快。

但就像 Jonathon 所说的那样,除非速度非常重要,否则不要为此烦恼。它只会降低可读性,而性能可以忽略不计。

I suspect using local variable is only a single reference (the value is on the stack), while using member variable is 2 reference (reference to this, which is on the stack, then reference to the variable itself, which is on the heap)

Depending on the system, either heap or stack access could be faster.

But like what Jonathon said, unless speed is very important, don't bother yourself with this. It will only reduce readability for negligible performance.

全部不再 2024-10-27 09:44:35

理论上来说,不会。访问“this._obj.Length”最终会生成如下代码:

mov eax, [ecx + offset_of_obj]
mov eax, [eax + offset_of_length]

其中“obj.length”最终会生成如下代码:

mov eax, [esp + offset_of_obj]
mov eax, [eax + offset_of_length]

在实践中,也许是,但可能不是。几乎每个 x86 调用约定都只有 3 个暂存寄存器“eax”、“ecx”和“edx”。所有其他寄存器必须先保存在堆栈上,然后才能更新。如果您有一个很长的函数,并且不需要访问“this”,那么 ecx 寄存器可以重新用于保存临时变量,从而减少需要发生的堆栈溢出量。但是,您必须将新值推送到堆栈上才能创建局部变量,因此它可以进行改进的场景是有限的。我会忽略谁告诉过你这件事。

In theory, no. Accessing "this._obj.Length" ends up generating code like:

mov eax, [ecx + offset_of_obj]
mov eax, [eax + offset_of_length]

where as "obj.length" ends up generating code like:

mov eax, [esp + offset_of_obj]
mov eax, [eax + offset_of_length]

In practice, maybe, but probably not. With virtually every x86 calling convention there are only 3 scratch registers "eax", "ecx", and "edx". All other registers must be saved on the stack before they can be updated. If you have a long function, and you don't need to access "this" then the ecx register could be repurposed to hold temporary variables, and could thus reduce the amount of stack spilling that needs to happen. But, you have to push new values on the stack in order to create the locals,so the scenarios where it would make an improvement are limited. I would ignore who ever told you that.

逆光下的微笑 2024-10-27 09:44:35

其中一些答案没有回答实际问题,另一些答案是错误的。通过 this.obj 访问成员变量需要取消引用堆栈上的元素。访问该引用的本地副本可以消除取消引用步骤。因此从理论上讲,如果没有 HotSpot,后者必须更加高效。然而,除非你对核反应或其他东西进行计时,否则差异将很小,而且每当我在商店里看到这种做法时,我都会反对这种做法。

Some of these answers haven't answered the actual question and others are wrong. Accessing a member variable via this.obj requires dereferencing an element on the stack. Accessing a local copy of that reference eliminates the dereference step. So in theory and absent HotSpot the latter has to be more efficient. However unless you are timing nuclear reactions or something the difference will be minimal, and I would deprecate the practice any time I saw it in my shop.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文