How can you interpret the code even inefficiently? (Theoretical)

OK, first of all, I don’t want any flamewar here or something like that. My larger question is more theoretical and will contain a few examples.

So, as I wrote, I cannot understand how an interpreted language can even be a little effective. And from the moment of its creation I will take Java as an example.

Let's get back to the days when there were no JIT compilers. Java has its own virtual machine, which is basically its hardware. You write the code, than it was compiled into bytecode to perform at least some work from the virtual machine. But considering how complex even a set of RISC commands can be at the hardware level, I can't even figure out how to do this on software emulated hardware.

I have no experience writing virtual machines, so I don’t know how to do it at the most efficient level, but I can’t think of anything more efficient than testing each instruction for adn compliance, than doing the corresponding actions. You know, something like: if(instruction=="something") { (do it) } else if(instruction=="something_diffrent"){ (do it) } , etc.

But that should be terribly slow. And yet, there are even articles that java was slow in front of JIT compilers, they still say that it is not so slow. But emulation requires the execution of several clock cycles of a real HW to execute a single bytecode instruction.

And yet even entire platforms are based on java. For example, Android. And in the first versions of Android there was no JIT compiler. They were interpreted. But shouldn't Android be terribly slow? And yet this is not so. I know, when you call any API function from the Android library, they are written in machine code, so they are efficient, therefore it helps.

But imagine you write your own sratch game engine using the API only for displaying images. You will need to do many copy operations of arrays, many calculations will be very slow when emulating.

And now some examples, as I promised. Since I mainly work with the MCU, I found the JVM for the Atmel AVR MCM. Thay states that the 8MHZ MCU can do 20K java optcodes per second. But since AVR can execute most instructions in one or two cycles, let's say 6,000,000 instructions on average. This gives us that a JVM without a JIT compiler is 300 times slower than machine code. So why become java so popular without a JIT compiler? Is this a too bad performance loss? I just do not understand. Thanks.

+4
source share
4 answers

We already have a byte code. On the old Apple II, the USCD p-system was very popular, which compiled Pascal into byte code that would be interpreted by an 8-bit 6502 that can run at 2 MHz. These programs ran fast enough.

The bytecode interpreter is usually based on a conversion table, not a chain of if/then/else . In C or C ++, this includes a switch . In essence, the interpreter will have the equivalent of an array of processing code and use the operation code in the byte code instruction as an array index.

It is also possible to have a bytecode that is higher than machine instructions, so a single bytecode instruction will translate to multiple, and sometimes multiple, machine code instructions. The bytecode that was created for a particular language can do this quite easily, since it only needs to match the control structure and data of that particular language. This stretches the overhead of interpretation and makes the interpreter more efficient.

An interpreted language is likely to have some speed limit compared to a compiled language, but this is often inconsequential. Many programs process input and output at the speed of a person, and this leaves enormous performance that can be wasted. Even a network-related program is likely to have much more processor power than it needs. There are programs that can use all the CPU efficiency that they can get, and for obvious reasons, they are usually not written in interpreted languages.

And, of course, there is the question of what you get for some kind of inefficiency, which may or may not matter. Interpreted language implementations are generally easier to port than compiled implementations, and actual byte code is often ported. It may be easier in a language to install higher-level functionality. This allows you to make the compilation step much shorter, which means that execution can start much faster. This can help improve diagnosis if something goes wrong.

+3
source

But shouldn't Android be terribly slow?

Define "terribly slow." This is a phone. Before dialing the second digit, it must process "Dial the first digit."

In any interactive application, the limiting factor is always the reaction time of a person. It can be 100 times slower and still be faster than the user.

So, to answer your question, yes, interpreters work slowly, but they are usually fast enough, especially when the hardware continues to grow.

Remember, when Java was introduced, it was sold as a web applet language (replaceable and now replaced by Javascript, which is also interpreted). Only after compiling JIT did it become popular on servers.

Bytecode interpreters can be faster than the if () s line using a jump table:

  void (*jmp_tbl)[256] = ...; /* array of function pointers */ byte op = *program_counter++; jmp_tbl[op](); 
0
source

There are two different ways to approach this issue.

(i) "why is it ok to run slow code"

As James has already mentioned, sometimes execution speed is not all that interests you. Many applications running in interpreted mode can be “fast enough”. You must consider how the code you write will be used.

(ii) "why is the inneficient code interpreted"

There are many ways to implement an interpreter. In your question, you're talking about the most naive approach: basically a large switch that interprets each JVM instruction as it reads.

But you can optimize this: for example, instead of looking at a single JVM instruction, you can look at their sequence and look for patterns for which you have more efficient interpretations. The Sun JVM actually performs some of these optimizations in the interpreter itself. At a previous job, the guy spent some time optimizing the interpreter this way, and interpreting Java bytecode worked noticeably faster after changing it.

But in modern JVMs that contain a JIT compiler, the interpreter is just a step until the JIT does its job, so people don’t spend so much time optimizing the interpreter.

0
source

12 MHz is the ATtiny, which is an 8-bit microprocessor. This means (for example) that the native Add command can only add two 8-bit numbers together to get a 9-bit result. JVM is basically a 32-bit virtual processor. This means that his add command adds two 32-bit numbers together to get a 33-bit result.

Thus, when you compare instruction rates, you should expect a decrease in learning speed of 4: 1 as an absolute minimum. In fact, although it is easy to simulate a 32-bit add-on with 4 8-bit add-ons (with media), some things do not scale at all like that. For example, according to an Atmel post, the application note , 16x16 multiplication, generating a 32-bit result, runs in ~ 218 clock cycles. The same application note shows 16/16 bit separation (getting an 8-bit result), working in 255 cycles.

Assuming that these scales are linear, we can expect that 32-bit versions of the multiplication will occupy ~ 425-450 clock cycles, and division will take 510 cycles. In fact, probably we should expect a bit of overhead, which will slow down even more - adding at least 10% to these estimates is likely to make them more realistic.

Bottom line: when you compare apples to apples, it becomes obvious that the significant difference in speed that you are talking about is not real at all (or does not apply to the JVM overhead).

0
source

All Articles