Timeline for Using compound statements ("{" ... "}" blocks) to enforce variable locality
Current License: CC BY-SA 3.0
17 events
when toggle format | what | by | license | comment | |
---|---|---|---|---|---|
May 23, 2017 at 12:40 | history | edited | CommunityBot |
replaced http://stackoverflow.com/ with https://stackoverflow.com/
|
|
Apr 12, 2017 at 7:31 | history | edited | CommunityBot |
replaced http://programmers.stackexchange.com/ with https://softwareengineering.stackexchange.com/
|
|
Nov 12, 2015 at 7:26 | comment | added | Luaan | You can always attach a debugger after the application starts, giving you the real instructions executing - I'm not actually using AOT, this is all based on JITter output. It's a bit harder to read than IL, of course. However, I don't doubt that Mono is still far behind the "real" CLR implementation - I know that it has many issues with memory management, it wouldn't be a surprise if it's assembly generation was also more limited. It does mean you have to write silly code once in a while if you're targeting Mono :) | |
Nov 11, 2015 at 18:44 | comment | added | user40980 | @Luaan as I run on a mac, the only visibility I have into IL is mono, which is a virtual machine much like the JVM. I don't get AOT compilation and optimizations - I can only see to the level of what gets passed to the JIT if it is done at all (and given the development environment for the mac for .net, that isn't a trivial thing). What I do see is that the IL layer allocates space for each distinct variable used within the entire scope of the method, and that is what is executed on my machine. | |
Nov 11, 2015 at 15:32 | comment | added | Luaan |
Don't stop at the IL level. When looking at the actual x86 assembly produced from that code, the locals don't exist anymore - just the simple "pass-by-ecx" code. IL is just a high-level intermediate language, it rarely looks anything like the resulting x86 code. In fact, locals are only rooted until they are no longer used - if the two locals existed, one would be reused regardless of scoping. Even replacing the constants with a Console.ReadLine avoids the stack entirely. IL is just a description of the program, not a bullet-list. IL uses the stack for everything, the x86 result doesn't.
|
|
Nov 11, 2015 at 3:42 | comment | added | user40980 | @5gon12eder I dug into it a bit more. While C and C++ register allocation is well done with AOT compilation, languages that go to a byte code appear to allocate space for all of the variables used within the scope of the method, even if the variables themselves have mutually exclusive sub-scopes. | |
Nov 11, 2015 at 3:41 | comment | added | user40980 | @ratchetfreak I've adjusted the code and shown both the .net IL and the decompiled method header for the JVM. They are both allocating space for all the inner scopes at the top of the method declaration, even if you aren't using them at the same time. | |
Nov 11, 2015 at 3:39 | history | edited | user40980 | CC BY-SA 3.0 |
added 2187 characters in body
|
Nov 10, 2015 at 21:31 | comment | added | user40980 |
@5gon12eder If you are allocating a dozen {} blocks within the scope of one method with a few variables in each, this could get expensive on the stack. Granted, there are other problems if you have a method with that much complexity in it. This just adds to the problems.
|
|
Nov 10, 2015 at 21:26 | comment | added | 5gon12eder | While I don't think that anything said in the answer or the comments is wrong, I wouldn't let these concerns guide my programming style (at least not at the expense of readability) unless I had evidence that there is a measurable effect in a particular case I care about. Register allocation is a very well studied field and your compiler is likely to do a much better job than you. | |
Nov 10, 2015 at 20:47 | comment | added | user40980 | @Deduplicator In Java, inlining is done by the JIT, not javac. So... it gets tricky again. I am curious though to dig into this and find out what is really going on with the breaking down the bytecode for a class that has this behavior and poking at the JIT internals to turn on debugging to see if I can tease out the "this is what is happening." Though again, this "work" thing during the day can be quite distracting. | |
Nov 10, 2015 at 20:44 | comment | added | Deduplicator | Well, if there's inlining going on, it's quite likely all variables for the function and all inlined functions are allocated in one go for efficiency. | |
Nov 10, 2015 at 15:05 | comment | added | user40980 |
@JAB that depends on the compiler. In gcc with similar code, both are allocated qux and baz are assigned just a register, and its the same register in each case. With .net IL and Java bytecode, it is likely to allocate the space and then allow JIT to do the escape analysis on it which is much harder to dig into.
|
|
Nov 10, 2015 at 15:02 | comment | added | user40980 | @ratchetfreak that was explored a bit in chat around that time and a bit awkward to extract. My example is really pushing the envelope of it (though I'll dig into the Java byte code and .net IL when I can more easily work on that without the distractions of this 'work' thing). | |
Nov 10, 2015 at 15:02 | comment | added | JAB |
"This allocates the space for three integers at the initialization of the function foo" Couldn't a compiler that accounts for variable liveness, observing that qux and baz are never in scope at the same time, just reuse the space? Variable names are just tools to assist the programmer, the compiler doesn't need to preserve them (and in most compiled languages, it doesn't unless you embed symbols for debugging).
|
|
Nov 10, 2015 at 14:59 | comment | added | ratchet freak | That analysis only considers different types in different blocks but doesn't test your variant. | |
Nov 10, 2015 at 14:50 | history | answered | user40980 | CC BY-SA 3.0 |