Skip to content

Commit edfbf2c

Browse files
committed
appendix: update dis example to reflect code changes in main text
1 parent d30c897 commit edfbf2c

File tree

1 file changed

+93
-90
lines changed

1 file changed

+93
-90
lines changed

learners/technical-appendix.md

Lines changed: 93 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,18 @@ The topics covered here exceed the level of knowledge required to benefit from t
1414

1515
You can use `dis` to view the bytecode generated by Python. The amount of bytecode more strongly correlates with how much code is being executed by the Python interpreter and hence how long it may take to execute. However, this is a crude proxy as it does not account for functions that are called and whether those functions are implemented using Python or C.
1616

17-
The pure Python search compiles to 82 lines of bytecode.
17+
The pure Python search compiles to 51 lines of bytecode. (Note that different versions of Python may produce slightly different bytecode than shown here.)
1818

1919
```python
2020
import dis
21+
import random
22+
23+
# Generate sample data
24+
N = 2500
25+
M = 2
26+
ls = [random.randint(0, int(N*M)) for i in range(N)]
2127

2228
def manualSearch():
23-
ls = generateInputs()
2429
ct = 0
2530
for i in range(0, int(N*M), M):
2631
for j in range(0, len(ls)):
@@ -31,65 +36,63 @@ def manualSearch():
3136
dis.dis(manualSearch)
3237
```
3338
```output
34-
11 0 LOAD_GLOBAL 0 (generateInputs)
35-
2 CALL_FUNCTION 0
36-
4 STORE_FAST 0 (ls)
37-
38-
12 6 LOAD_CONST 1 (0)
39-
8 STORE_FAST 1 (ct)
40-
41-
13 10 LOAD_GLOBAL 1 (range)
42-
12 LOAD_CONST 1 (0)
43-
14 LOAD_GLOBAL 2 (int)
44-
16 LOAD_GLOBAL 3 (N)
45-
18 LOAD_GLOBAL 4 (M)
46-
20 BINARY_MULTIPLY
47-
22 CALL_FUNCTION 1
48-
24 LOAD_GLOBAL 4 (M)
49-
26 CALL_FUNCTION 3
50-
28 GET_ITER
51-
>> 30 FOR_ITER 24 (to 80)
52-
32 STORE_FAST 2 (i)
53-
54-
14 34 LOAD_GLOBAL 1 (range)
55-
36 LOAD_CONST 1 (0)
56-
38 LOAD_GLOBAL 5 (len)
57-
40 LOAD_FAST 0 (ls)
58-
42 CALL_FUNCTION 1
59-
44 CALL_FUNCTION 2
60-
46 GET_ITER
61-
>> 48 FOR_ITER 14 (to 78)
62-
50 STORE_FAST 3 (j)
63-
64-
15 52 LOAD_FAST 0 (ls)
65-
54 LOAD_FAST 3 (j)
66-
56 BINARY_SUBSCR
67-
58 LOAD_FAST 2 (i)
68-
60 COMPARE_OP 2 (==)
69-
62 POP_JUMP_IF_FALSE 38 (to 76)
70-
71-
16 64 LOAD_FAST 1 (ct)
72-
66 LOAD_CONST 2 (1)
73-
68 INPLACE_ADD
74-
70 STORE_FAST 1 (ct)
75-
76-
17 72 POP_TOP
77-
74 JUMP_FORWARD 1 (to 78)
78-
79-
15 >> 76 JUMP_ABSOLUTE 24 (to 48)
80-
>> 78 JUMP_ABSOLUTE 15 (to 30)
81-
82-
13 >> 80 LOAD_CONST 0 (None)
83-
82 RETURN_VALUE
39+
9 RESUME 0
40+
41+
10 LOAD_CONST 1 (0)
42+
STORE_FAST 0 (ct)
43+
44+
11 LOAD_GLOBAL 1 (range + NULL)
45+
LOAD_CONST 1 (0)
46+
LOAD_GLOBAL 3 (int + NULL)
47+
LOAD_GLOBAL 4 (N)
48+
LOAD_GLOBAL 6 (M)
49+
BINARY_OP 5 (*)
50+
CALL 1
51+
LOAD_GLOBAL 6 (M)
52+
CALL 3
53+
GET_ITER
54+
L1: FOR_ITER 56 (to L5)
55+
STORE_FAST 1 (i)
56+
57+
12 LOAD_GLOBAL 1 (range + NULL)
58+
LOAD_CONST 1 (0)
59+
LOAD_GLOBAL 9 (len + NULL)
60+
LOAD_GLOBAL 10 (ls)
61+
CALL 1
62+
CALL 2
63+
GET_ITER
64+
L2: FOR_ITER 24 (to L4)
65+
STORE_FAST 2 (j)
66+
67+
13 LOAD_GLOBAL 10 (ls)
68+
LOAD_FAST 2 (j)
69+
BINARY_SUBSCR
70+
LOAD_FAST 1 (i)
71+
COMPARE_OP 88 (bool(==))
72+
POP_JUMP_IF_TRUE 2 (to L3)
73+
JUMP_BACKWARD 18 (to L2)
74+
75+
14 L3: LOAD_FAST 0 (ct)
76+
LOAD_CONST 2 (1)
77+
BINARY_OP 13 (+=)
78+
STORE_FAST 0 (ct)
79+
80+
15 POP_TOP
81+
JUMP_BACKWARD 54 (to L1)
82+
83+
12 L4: END_FOR
84+
POP_TOP
85+
JUMP_BACKWARD 58 (to L1)
86+
87+
11 L5: END_FOR
88+
POP_TOP
89+
RETURN_CONST 0 (None)
8490
```
8591

86-
Whereas the `in` variant only compiles to 54.
92+
Whereas the `in` variant only compiles to 33.
8793

8894
```python
89-
import dis
90-
9195
def operatorSearch():
92-
ls = generateInputs()
9396
ct = 0
9497
for i in range(0, int(N*M), M):
9598
if i in ls:
@@ -98,43 +101,43 @@ def operatorSearch():
98101
dis.dis(operatorSearch)
99102
```
100103
```output
101-
4 0 LOAD_GLOBAL 0 (generateInputs)
102-
2 CALL_FUNCTION 0
103-
4 STORE_FAST 0 (ls)
104-
105-
5 6 LOAD_CONST 1 (0)
106-
8 STORE_FAST 1 (ct)
107-
108-
6 10 LOAD_GLOBAL 1 (range)
109-
12 LOAD_CONST 1 (0)
110-
14 LOAD_GLOBAL 2 (int)
111-
16 LOAD_GLOBAL 3 (N)
112-
18 LOAD_GLOBAL 4 (M)
113-
20 BINARY_MULTIPLY
114-
22 CALL_FUNCTION 1
115-
24 LOAD_GLOBAL 4 (M)
116-
26 CALL_FUNCTION 3
117-
28 GET_ITER
118-
>> 30 FOR_ITER 10 (to 52)
119-
32 STORE_FAST 2 (i)
120-
121-
7 34 LOAD_FAST 2 (i)
122-
36 LOAD_FAST 0 (ls)
123-
38 CONTAINS_OP 0
124-
40 POP_JUMP_IF_FALSE 25 (to 50)
125-
126-
8 42 LOAD_FAST 1 (ct)
127-
44 LOAD_CONST 2 (1)
128-
46 INPLACE_ADD
129-
48 STORE_FAST 1 (ct)
130-
>> 50 JUMP_ABSOLUTE 15 (to 30)
131-
132-
6 >> 52 LOAD_CONST 0 (None)
133-
54 RETURN_VALUE
104+
1 RESUME 0
105+
106+
2 LOAD_CONST 1 (0)
107+
STORE_FAST 0 (ct)
108+
109+
3 LOAD_GLOBAL 1 (range + NULL)
110+
LOAD_CONST 1 (0)
111+
LOAD_GLOBAL 3 (int + NULL)
112+
LOAD_GLOBAL 4 (N)
113+
LOAD_GLOBAL 6 (M)
114+
BINARY_OP 5 (*)
115+
CALL 1
116+
LOAD_GLOBAL 6 (M)
117+
CALL 3
118+
GET_ITER
119+
L1: FOR_ITER 20 (to L3)
120+
STORE_FAST 1 (i)
121+
122+
4 LOAD_FAST 1 (i)
123+
LOAD_GLOBAL 8 (ls)
124+
CONTAINS_OP 0
125+
POP_JUMP_IF_TRUE 2 (to L2)
126+
JUMP_BACKWARD 15 (to L1)
127+
128+
5 L2: LOAD_FAST 0 (ct)
129+
LOAD_CONST 2 (1)
130+
BINARY_OP 13 (+=)
131+
STORE_FAST 0 (ct)
132+
JUMP_BACKWARD 22 (to L1)
133+
134+
3 L3: END_FOR
135+
POP_TOP
136+
RETURN_CONST 0 (None)
134137
```
135138

136139
A naive assessment of how expensive two functions are can be carried out with this comparison.
137-
However this method of displaying bytecode only shows bytecode for the requested function, so it is not clear how expensive called function's will be or higher level changes to an algorithm which could reduce the number of iterations or similar.
140+
However this method of displaying bytecode only shows bytecode for the requested function, so it is not clear how expensive called functions will be or higher level changes to an algorithm which could reduce the number of iterations or similar.
138141

139142
## Hardware Level Memory Accesses
140143

0 commit comments

Comments
 (0)