Thanks for your detailed response. I can see you've put a lot of thought into it.
Unfortunately I won't be able to address all your points. Many of which I'm not interested in disagreeing with.
GyroVorbis wrote:christo wrote:Arguing that static compilation will not ever be completely replaced by dynamic compilation is like saying horse and cart will never be completely replaced by cars. It is technically true but despite what may have one time been seen as a neck-and-neck race, there is no competition and no debate about superior performance.
There are some glaring flaws with your metaphor.
A "car" and a "horse" are two linearly independent entities that fulfill the same need. A car is not built upon a horse. A car does not run on the same underlying architecture as a horse.
A dynamically compiled language not only
runs on the same architecture as a statically compiled language, but it is
built upon a statically compiled language. Java's JIT compiler is written in C. Last I heard, Microsoft's JIT compiler in the .NET framework was written in C++. You literally cannot have dynamically compiled language without a statically compiled language. Performance, ease-of-use, and any other argument you have in favor of one another aside, C# and Java both NEED C/++ to do their job. I don't believe a car requires a horse to do its job.
Nonsense. There's absolutely no need for C or C++ to implement Java. You can implement the entire Java stack in Java including native compilation. In fact a lot of early Java development, including runtimes, compilers and development environments, such as those written by OTI were written in Smalltalk, an even more dynamic runtime than Java.
GyroVorbis wrote:
And there are still some glaring problems with your assumptions. I hate to use the term "you computer science majors," but I'm going to. You computer science majors seem to be forgetting some EXTREMELY important architecture that literally invalidates all of your arguments. How exactly do things like memory-mapped external hardware factor into your grand "JIT compilation global domination" schemes? The concept of a pointer literally doesn't exist within JIT compiled languages. You don't have access to memory addresses to perform low-level communication with hardware. Guess what CAN communicate with such hardware? Statically compiled C/++.
I think you're making specific arguments about Java. JIT compiled languages can support pointer arithmetic. JIT just means bundling the compiler tool chain into the executable. It doesn't require managed memory. And managed memory does not preclude pointer arithmetic. The reason modern managed runtimes and their languages don't have pointer arithmetic is because its a major source of bugs, especially security bugs.
GyroVorbis wrote:
You have no direct access to things such as DMA devices for transferring data to external devices. How do you think polygons and data are submitted to your GPU in the case of video games?
You have no direct access to CPU registers to push and pop the state of the CPU onto the stack for context switching between two running processes. You have no access to translation lookaside buffers (TLBs) to implement a page table. How can you write an OS in a JIT-compiled language?
You're running through a list of low level things that are typically not bound in managed runtimes. You can make this list as long as you like, it doesn't imply that deciding how those resources are used will be done better by a human at coding time than a runtime at execution time and it does not preclude the development of languages and runtimes that expose those hardware features such that they can be manually managed when humans can do a better job.
I think you'll make faster progress if you recognise that the only thing executing is native code - in either case. I'm trying to correct your core misconception which is that there is some kind of lineage or hierarchy between static compilation and dynamic runtimes and that dynamic runtimes pay a tax as they execute by "going down the levels". You conclude that C++, having fewer of these "levels" (and assembly having fewer again) is therefore faster. Your premise is incorrect, so your conclusion is not supported by your reasoning.
GyroVorbis wrote:
Now lets take the argument one step further and look at why the argument for "JIT compilation completely replacing static compilation" is completely impossible with modern architectures. A processor is a finite state machine executing a series of COMPILED, BINARY instructions/opcodes. When a computer/processor boots up, it loads a series of instructions to begin executing (your bios or your operating system). These instructions are already there. Statically precompiled. It's inconceivable that a processor could load instructions (that it cannot understand) to begin dynamically compiling an OS. There would still have to be some form of statically precompiled code sitting there to tell the processor to load the JIT compiler. Once again, with a modern, assembly opcode executing CPU it is absolutely impossible to not have some form of static compilation present for dynamic compilation. Static compilation is a BUILDING block that dynamic compilation REQUIRES.
It's very interesting that you use the phrase "modern architectures". Increasingly, modern laptop, desktop and server CPUs in the post Pentium era actually do exactly what you say they do not. Even in the case of an assembly program written and executed without an operating system, just so we can be nice and clean and get away from these confusing notions of "c++ wrappers" as you call them and compilers, the CPU does not execute a series of instructions as they have been compiled. Way back in the before time, this is how CPUs worked but these days things are much more complex.
When the preconditions are met, these CPUs can reorder instructions, unroll loops and start executing branches before the test condition has been executed. In essence the modern CPU is doing EXACTLY what a modern managed runtime tries to do. They try to optimise the code that was written by the programmer using information about the current execution state. There are some differences about what can be done but when combining increasingly wide multicore dies with increasing numbers of levels of cache, very long pipelines, hyperthreading etc. it all amounts to the same thing. The computer knows better than the programmer.
GyroVorbis wrote:
Until we have CPUs that literally decode .NET IL code or Java Bytecode in hardware (and then either execute them directly or translate them into native opcodes), it is a physical impossibility that a dynamic compilation will replace static compilation.
These CPUs *have* already been developed back in the 90s (e.g. JavaStation) but having such CPUs is not sufficient to ensure faster execution (as you would no doubt know). What's interesting is that they are not necessary either.
If you or I were to implement a Java VM this weekend, we would interpret the bytecode. This is how the first runtimes were written because it's much easier to implement. However, on modern VMs such as Hotspot, the .NET CLR, the Visualworks Smalltalk VM and the V8 JavaScript VM (3 out of 4 developed by the same team in Denmark over the past 20 years) this simple interpreted mode is not how they work. IL and Java bytecode are compiled into native code multiple times with different optimisations (this can depend on VM config). IL/bytecode is basically a more convenient source format, it's literally an Intermediate form for the code before it is recompiled to native.
GyroVorbis wrote:
And what if it did? What if hardware really did decode the intermediate code? Then wouldn't that intermediate code become statically compiled code, since it is directly executed and was originally compiled to IL code? It's a recursive, never-ending argument that all boils down to the fact that dynamic compilation not only requires, but is literally built upon static compilation.
I think the strongest form of your argument is that static compilation is a special case of dynamic compilation where the compilation is only done once and done without the benefit of for any execution time data. Only in that sense can you say that dynamic compilation is built upon static compilation. Is this your point or am I misunderstanding?
GyroVorbis wrote:
"Oh, sure. There will always be those few little reasons that we will need static compilation, but they will be very few and far between." Okay, true. I agree. Then stop saying dynamic compilation will replace static compilation. The truth of the matter is that these "very few and far between reasons" that you seem to be neglecting in your higher-level computer-science-y view of the world of computing is ALL that people like lower level computer and electrical engineers working with architecture, operating systems, hardware-software integration, GPUs, external processors, or even the talented dudes WRITING your JIT compiler are concerned with. If you feel comfortable enough to say that "dynamic compilation will FULLY replace static compilation," then I feel comfortable enough stereotyping you as a typical computer scientist who is blissfully unaware of anything lower-level than your own level of abstraction.
You're putting quite alot of words in my mouth here. It's not helping that you keep using quotes around your own words to debate with a stereotype of me.
Saying that JITs have to be written in static languages is like saying compilers have to be written in assembly. It's just not true. It happens in many cases because of a bootstrap benefit that has more to do with other features of the language rather than that they are statically compiled.
I'll gladly write as large as you like that C++ is currently faster than Java because of a lot of the things you have written, but I'd like to correct a number of things just in case you're interested in reaching mutual understanding.
1. I'm not saying that Java will replace C++ completely or that Java or the current generation of dynamic runtimes will replace the current static compilation tool chains completely.
2. I'm comparing static vs dynamic compilation. If you don't have parity in the millions of other important features, such as hardware bindings, then you're in danger of mixing up unrelated effects when you compare different stacks. Garbage collection is not required for JIT. All the hardware bindings you were talking about are necessary in order for the dynamic runtime to optimise for them. Java/.NET and C++ are different in many ways, not just the static compilation and dynamic compilation axis.
3. I'm not defending Java for game development. The decision to use a language/stack/toolchain should be based on a range of things that are important to the project. C++ is a fine choice for your project! Perhaps it's optimal.
4. Static compilation provides superior performance within a particular envelope of factors. Perhaps this is where we can agree. If the hardware architecture has a primitive tool chain and nobody has developed advanced dynamic recompiling runtimes on them, then there is no choice but to use static compilation. It's a lot of work to develop a JIT. It's like comparing the job of racing a horse against a box full of Ferrari parts. If you can't get hold of a car then a horse is great.
5. Similarly the compiler contains a memory and a CPU overhead which is not in all cases outweighed by the benefits brought by runtime optimisations. This is your point, however I think you overstate it. Java runs in Blu Ray players, TVs, credit-card sized form factors. Having said that, I can imagine just as many microcontrollers and small CPUs that wouldn't support Java. I would argue that this doesn't mean no dynamic compilation runtime could run on it, but I do agree there will be certain cases where hardware limitations will prefer static compilation because that's work the target machine doesn't have to do.
Mind you, those machines are becoming more and more marginal. Phones and tablets are already multicore and plenty capable of managed runtimes. Look at Android. If you want to write code for washing machines I think you can probably avoid managed runtimes and JIT compilation. This is where the horse comes into its own.
Now imagine writing your own JIT.
Dirty low down hacker style (I aint no CS major!). Here's your JIT debut:
Imagine your C++ project. Imagine you link a compiler into it and all the source code. Imagine you ship the source code, the compiler and a wrapper script. All without the user knowing. When the user runs the game, it randomly chooses a set of compiler options, compiles and executes that code, keeping some metrics about framerates or whatever. Then after running for 10 minutes, the program recompiles itself with different options, logs different metrics and keeps doing this until it finds the optimal set of options that provide the best framerate. Somehow you manage to hide all this from the user using "clever hacking tricks" like scheduling the work to occur when the user is looking at the menu screen or whatever. Is this crazy? Hell yeah! But it shows that you could get better performance from dynamic compilation. Now imagine the user upgrades their CPU the next week and the optimal settings are different. When you wrote the code you didn't know what would run best on their new CPU because it didn't exist then. It's brand new! But now your game runs faster than it would have because it chooses the compilation options that are optimal for that user situation. What's really cool is that when that user's little brother plays the optimal compiler options are different because the little brother never goes past level 3 in the game where the hellish FP ops are done because of all the 3d sound modelling you have on that level.
Does anyone ship their games like this? No. Why? Cost/benefit ratio. A million other reasons. Does this mean it's incorrect? No. If you think this is too theoretical, then at least admit that it is true as long as the compiler in the executable is capable of utilising optimal (according to your own metrics, e.g. framerate) compilation techniques that are local to both the target architecture AND the set of currently executing code paths. Acknowledge that these things cannot possibly be known ahead of time (statically).