Understand beyond "Hello World". How the machine interprets the code.
Tags: python, computerscience, codenewbie
Published: 2020-02-19 19:40:17


> **Hello World!**

That's our favorite phrase to print every time we start learning a new programming language, but here I try to explain how the interpreter understands the code. 

Python compiles the program to the `bytecode` which is an internal representation of python to the interpreter.

Consider the following code which takes two function arguments that returns the multiplication
of those two values,

```python
def multiplication(value1, value2):
      multiplied_value = value1 * value2
      return multiplied_value
```

The raw compiled byte code of the function gives,
```python
multiplication.__code__.co_code
b'|\x00|\x01\x14\x00}\x02|\x02S\x00'
```
and the bytecodes as numbers are,
```python
list(multiplication.__code__.co_code)
[124, 0, 124, 1, 20, 0, 125, 2, 124, 2, 83, 0]
```

It's difficult(almost impossible) for a human to read and understand the bytecode conversion directly. We have a python package(called <a href="https://docs.python.org/2/library/dis.html" target="_blank">`dis`</a>) that is available to convert the bytecode into human-readable instruction.

On converting the above bytecode(production function) into machine instruction(as compiler sees it):
```python
import dis
dis.dis(multiplication)

2           0 LOAD_FAST                0 (value1)
            2 LOAD_FAST                1 (value2)
            4 BINARY_MULTIPLY
            6 STORE_FAST               2 (multiplied_value)

3           8 LOAD_FAST                2 (multiplied_value)
            10 RETURN_VALUE
```
---

<h2>How it looks inside virtual machine</h2>

>Cpython uses the stack-based virtual machine where the instructions take place using push and pop(stack data structure)

Three types of stacks:
* <b> Call stack </b>- it always have one active item which is called 'frame'. main of the program where it belongs at the bottom. Every function call pushes a new frame to the call stack and each time the function returns, its frames get popped out of the stack
* Each frame there is an <b>evaluation stack</b>, (i.e, data stack) it's mainly responsible for the execution of python function, pushing the frames to stack, executing them and popping them.
* Each frame contains <b>block stack</b>, which helps the program to keep track of control structures ie blocks, it gets popped off every time program or loop comes out of the block.

Based on the stacks if we see out bytecode listings for `multiplication()` function it executes in the order:
* <b> LOAD_FAST</b> for the value 1 and value 2 gets pushed sequentially to the top of the evaluation stack.
* <b> BINARY_MULTIPLY </b> is the operation expression representation on the evaluation stack which is explained <a href="https://docs.python.org/2/library/dis.html#opcode-BINARY_MULTIPLY" target="_blank">here</a>, which multiplies TOS 1(Top Of Stack) and TOS 2.
* <b> STORE_FAST </b> gets pushed to the top of stack after the <i>BINARY_MULTIPLY</i> operation, which stores the result in a local variable(`multiplied_value`) as per the funtion definition
* <b> LOAD_FAST</b> instruction loads the variable(`multiplied_value`) from the local memory on top of the evaluation stack before returning the value
* <b> RETURN_VALUE </b> instruction, returns the TOS position to the caller function so that instruction continues from there and it goes on.


There is always the question of why python execution is faster inside the function unlike outside.
just like the scenario mentioned below,

case 1:

```python
def main():
    for i in range(10**9):
        pass

if __name__ == '__main__':
    main()
```

The execution time for the above script is,
> 22.35s user 0.15s system 94% cpu 23.838 total

bytecode instructions for the disassembly part(inside the function) looks like,

```python
2           0 SETUP_LOOP              16 (to 18)
            2 LOAD_GLOBAL              0 (range)
            4 LOAD_CONST               1 (1000000000)
            6 CALL_FUNCTION            1
            8 GET_ITER
      >>   10 FOR_ITER                 4 (to 16)
            12 STORE_FAST               0 (i)

3          14 JUMP_ABSOLUTE           10
      >>   16 POP_BLOCK
      >>   18 LOAD_CONST               0 (None)
            20 RETURN_VALUE
```

case 2:
```python
for i in range(10**9):
    pass
```
> 41.65s user 0.34s system 85% cpu 49.053 total

```python
1           0 SETUP_LOOP              16 (to 18)
            2 LOAD_NAME                0 (range)
            4 LOAD_CONST               0 (1000000000)
            6 CALL_FUNCTION            1
            8 GET_ITER
      >>   10 FOR_ITER                 4 (to 16)
            12 STORE_NAME               1 (i)

2          14 JUMP_ABSOLUTE           10
      >>   16 POP_BLOCK
      >>   18 LOAD_CONST               1 (None)
            20 RETURN_VALUE
```

If it is called directly without any function, the execution time is observed that it takes almost double the time to complete.


But what makes it different when the loop is executed inside the function like in the first case? Digging the bytecode instructions for these scenarios,
`STORE_FAST` is used in case 1 which makes the execution faster but the latter uses `STORE_NAME` instruction.

---

*why can't CPython has the implementation of STORE_FAST everywhere if it can improve the performance?*

&nbsp; &nbsp; STORE_FAST is used for referencing the local variables whereas the STORE_NAME is referred to as the global one.

if you look <a href="https://hg.python.org/cpython/file/45b1ae1ef318/Python/ceval.c" target="_blank">in-depth</a>, you can see the implementation for global variables are in dictionary(which takes dictionary name, key element, and value as its argument) and for local variables are in lists with variable as its index(so that makes the lookup pretty fast). The convincing reason behind this implementation is assumed to be the local variable address will get flushed out after the termination of a particular loop/function but the global variable has to keep track of variable with class and its object properties.

---

If you have reached till here. Special thanks for spending your time to read.

NOTE: This blog is originally published on my <a href="https://www.sibis.dev">personal website</a>. I do have lot of other content. Consider reading it - https://sibis.dev