specifying optimizable global variables via __fix__.
so far due to the restriction of python runtime, an embedded jit compiler cannot easily aware the change of global variables and trigger recompilation. To make the problem simpler, we request users to fill constant global variables.
__fix__ = ['f1', 'f2']
@jit.aware
def f1(x):
return x + x
@jit.aware
def f2(x):
if x < 3:
y = x * 10
else:
y = f1("aaa")
return y + y
If f1 is not provided in __fix__, we cannot assume f2 is referencing the function object f1 visually defined with def f1. That is because it's possible to change f1 later in python. Beside of providing __fix__, users should maintain the constant property of all variables in __fix__.
the capability of dynjit compiler.
method lookup specialisation.
code like a = []; a.append(1) will be at least more performant than a = []; $(list.append)(a, 1), $ is not a real language construct, it means the splicing of constants, hence we don't need tricks like a = []; append = a.append; # use append later any more.
Union split(supercompiler). The overhead of functions that returns a value of union type is only a single runtime type check.
a = f(...) # if a can be int or float or str
return a + a # by dynjit, '+' operator here
# is not the generic one, but the specialised one
Bool split(supercompiler).
Every boolean value will be specialised to True or False and help to specialise control flows.
Control flow specialisation.
if expression:
# if expression can be partially evaluated to True(False),
# then statically choose the first(second) arm
...
eliminate use of shallow data structures.
a = ...
return a[1]
If a partially evaluated to a static value (1, 2, 3), then the code is identical to
return 2
This is much more powerful than the usual constant propagation due to our online partial evaluation.
For termination guarantee, we cannot optimize use of nested/mutable data structures like [1, 2, 3] and ((1, 2, 3), ()).
P.S: after getting dynjit ir we can emit LLVM IR code or C code to gain more kinds of optimizations. dynjit compiler itself is used for handling dynamisms of runtime.
@aware
decoratorspecifying optimizable global variables via
__fix__
.so far due to the restriction of python runtime, an embedded jit compiler cannot easily aware the change of global variables and trigger recompilation. To make the problem simpler, we request users to fill constant global variables.
If
f1
is not provided in__fix__
, we cannot assumef2
is referencing the function objectf1
visually defined withdef f1
. That is because it's possible to changef1
later in python. Beside of providing__fix__
, users should maintain the constant property of all variables in__fix__
.the capability of dynjit compiler.
method lookup specialisation.
code like
a = []; a.append(1)
will be at least more performant thana = []; $(list.append)(a, 1)
,$
is not a real language construct, it means the splicing of constants, hence we don't need tricks likea = []; append = a.append; # use append later
any more.Union split(supercompiler). The overhead of functions that returns a value of union type is only a single runtime type check.
Bool split(supercompiler).
Every boolean value will be specialised to
True
orFalse
and help to specialise control flows.Control flow specialisation.
eliminate use of shallow data structures.
If
a
partially evaluated to a static value(1, 2, 3)
, then the code is identical toThis is much more powerful than the usual constant propagation due to our online partial evaluation. For termination guarantee, we cannot optimize use of nested/mutable data structures like
[1, 2, 3]
and((1, 2, 3), ())
. P.S: after getting dynjit ir we can emit LLVM IR code or C code to gain more kinds of optimizations. dynjit compiler itself is used for handling dynamisms of runtime.