Quote: " Not too sure what you're confirming here. My argument, or Ben's."
Ben.
Quote: " I guess that's true. But that doesn't really depend on the language as much as it does it's features (or functions if you want)."
If you strip out functions and purely compare memory access / math / logic / comparisons and control structures, then a purely
interpreted VM hasn't got a hope a hell against even the sloppiest native code. JIT is another thing however.
When then VM interprets opcodes, it basically pulls it from memory, calls the decoder (via jump table hopefully) and loops back.
Ie A bit like this ( pseudo decoder)
For Opcodes =0 to CycleCount
instruction = ProgramMemory( ProgramCount)
ProgramCount++
Gosub OpcodeTable( Instruction )
next
So just for examples sake, lets assume this is a perfect world and the decoder loop executes one byte code call (just call) in 5 clock cycles. If the VM's instruction set was a 1 to 1 mapping with the host cpu (like you'd see in an emulator say) the best we could possibly hope for in terms of performance would be a 1/5th (cough more like a 1/10th.) of the native execution.
Obviously to improve performance, the VM instruction set needs to be aimed at a higher level than a 1 to 1 mapping, but low enough to retain flexibility. Above that, the compiler needs to be intelligent enough to streamline code generation, so it only outputs necessary opcodes.
The fantasy of every interpreter writer is to come as close to native code execution as possible (across the board), but without JIT that's just fantasy.
Quote: "I guess speed comes down to optimization really. Something that I don't know if assembly does, and I assume machine code doesn't "
Yes assemblers have instruction level optimizations. Which not only includes trivial exchanges, but often the ability to lay instructions/memory accesses out in more processor friendly manner.