Which code is more CPU / memory efficient when used with Garbage Collected language?

I have these two dummy codes (let them be written in both Java and C #, all variables are local):

Code 1:

int a; int b = 0; for (int i = 1; i < 10 ; i++) { a = 10; b += i; // a lot of more code that doesn't involve assigning new values to "a" } 

Code 2:

 int b = 0; for (int i = 1; i < 10 ; i++) { int a = 10; b += i; // a lot of more code that doesn't involve assigning new values to "a" } 

At first glance, I would say that both codes consume the same amount of memory, but Code 1 is more efficient from the CPU point of view, since it creates and allocates the variable a only once. Then I read that garbage collectors are extremely efficient to such an extent that code 2 will be more efficient for Memory (and CPU?): Saving the variable a inside the loop makes it a Gen0, so this will be garbage collection before variable b .

Thus, when used with the garbage collection language, code 2 is more efficient. I'm right?

+5
source share
2 answers

A few points:

  • ints (and other primitives) are never allocated in a heap. They live directly on the thread stack, “distribution” and “release” are simple pointer movements and occur once (when the function is introduced and immediately after returning), regardless of the scope.

  • frequently accessed primitives are usually stored in a register for speed, again, regardless of scope.

  • in your case a (and possibly b , as well as the whole cycle) will be "optimized", the optimizer is smart enough to detect a situation where the value of a variable changes but is never read and skips redundant operations. Or, if there is code that actually looks at a but does not change it, it will most likely be replaced by the optimizer with a constant value of "10", which will simply be displayed inside where a refers.

  • New objects (if you did something like String a = new String("foo") , for example, instead of int) are always allocated to the younger generation and only passed to the old gene after they survived the collection. This means that for most cases when an object is distributed within a function and never referenced from the outside, it never falls into the old gene, regardless of its exact scope, if your heap structure does not need to be configured.

  • As indicated in the commentary, sometimes the VM may decide to allocate a large object directly to the old gen (this is also true for java, and not just for .net), so the above point applies only in most cases, but not always. However, in connection with this issue, this does not matter, because if a decision is made on the distribution of an object in the old gene, it is made regardless of the size of its original link in any case.

In terms of performance and memory, your two fragments are identical. However, from the point of view of readability, it is always useful to declare all variables in the narrowest scope.

+38
source

Before executing the code in fragment 2, it will be converted to look like the code in fragment 1 behind the scenes (be it the compiler or the runtime). As a result, the performance of the two fragments will be the same, because at some point they will be compiled into the same code.

Note that for very short variables, it is actually quite possible that they do not have allocated memory for them at all. They can be fully stored in the register, including memory allocation.

+18
source

Source: https://habr.com/ru/post/1210750/


All Articles