nibbler is a runtime bytecode optimizer.
It explores the concept of using existing Python syntax features such as type annotations and decorators to speed up code execution by running additional bytecode optimization passes that make use of runtime context provided through these means.
inline
Inlines parameter-less calls to functions that are decorated with@nibbler.inline
.constantize_globals
Copies the value of globals that were marked constant (with aConstant
type annotation or with the@nibbler.constant
decorator) into theco_consts
tuple of functions that would normally have to access the global namespace, which speeds up variable access. This also applies to builtins (any
,all
,print
, ...).precompute_conditionals
Strips out conditionals that test constants which the peephole optimizer doesn't pick up on.global_to_fast
Transforms global variable loads to local variable loads if a local variable with the same name exists (mostly a cleanup pass forinline
)peephole
Invokes the Python peephole optimizer with additional context.
from typing import Iterable
from nibbler import Constant, Nibbler
DEBUG: Constant[bool] = False
nibbler = Nibbler(globals())
@nibbler.inline
def square(number: int, base: int) -> int:
result = number ** base
return result
@nibbler.nibble
def sequential_square(numbers: Iterable[int]) -> int:
product = 0
base = 2
for number in numbers:
square()
if DEBUG:
print(result)
product += result
print(f"Result: {product}")
return product
sequential_square(range(4))
↓
Result: 14
Examining the function bytecode reveals which optimizations nibbler has performed:
2 0 LOAD_CONST 1 (0)
2 STORE_FAST 1 (product)
3 4 LOAD_CONST 2 (2)
6 STORE_FAST 2 (base)
4 8 SETUP_LOOP 28 (to 38)
10 LOAD_FAST 0 (numbers)
12 GET_ITER
>> 14 FOR_ITER 20 (to 36)
16 STORE_FAST 3 (number)
5 18 LOAD_FAST 3 (number)
20 LOAD_FAST 2 (base)
22 BINARY_POWER
24 STORE_FAST 4 (result)
6 26 LOAD_FAST 1 (product)
28 LOAD_FAST 4 (result)
30 INPLACE_ADD
32 STORE_FAST 1 (product)
34 JUMP_ABSOLUTE 14
>> 36 POP_BLOCK
8 >> 38 LOAD_CONST 5 (<built-in function print>)
40 LOAD_CONST 3 ('Result: ')
42 LOAD_FAST 1 (product)
44 FORMAT_VALUE 0
46 BUILD_STRING 2
48 CALL_FUNCTION 1
50 POP_TOP
9 52 LOAD_FAST 1 (product)
54 RETURN_VALUE
- The
square
function was inlined (inline
) (18-24) - Conditional (
if DEBUG
) was stripped out, becauseDEBUG
was declared a constant (precompute_conditionals
) (26) - The
print
function was promoted to a function-level constant (constantize_globals
) (38)
pip3 install nibbler
- Is this production ready?
Hell no. - Why is it called nibbler?
¯\_(ツ)_/¯