Native data¶
This example shows which Python values cross the boundary as machine
values, and which stay boxed. ppy explain says which is which, and why:
- Scalars, fixed-size tuples, and all-scalar classes are handed over flat.
- A
list[float]is copied into a buffer on the way in. - Everything else stays on the Python side.
Run it¶
Tuples¶
@ppy.pure
def norm2(point: tuple[f64, f64, f64]) -> f64:
return point[0] * point[0] + point[1] * point[1] + point[2] * point[2]
@ppy.pure
def centroid(a: tuple[float, float], b: tuple[float, float]) -> tuple[float, float]:
return ((a[0] + b[0]) * 0.5, (a[1] + b[1]) * 0.5)
A tuple[f64, f64, f64] is three doubles in the ABI. Returning a tuple is
two result slots, not a heap object, so centroid allocates nothing on the
native path. Array[int, 3] is the same idea for a small fixed container.
Lists¶
dot takes two list[float] and total a list[i64]. Native code cannot
walk a Python list, so a homogeneous list is copied into a contiguous
buffer at the call. That is why a borrowed
Buffer[T] is the faster spelling.
total([10**30, 1]) shows the guard on the element. The first value does
not fit an i64, the boundary refuses, and the Python body prints the exact
sum.
To see the representation chosen for every parameter:
What it prints¶
python native_data.ppy, ppy native_data.ppy, ppy run native_data.ppy
Read on: Tuples · Value classes · Native lowering
native_data.ppy is hand-written; there is no .py source and no conversion
step.
08_native_data/native_data.ppy¶
import ppy
from ppy import Array, f64, i64
@ppy.pure
@ppy.opt(3)
def dot(a: list[float], b: list[float]) -> float:
product: float = 0.0
for i in range(len(a)):
product += a[i] * b[i]
return product
@ppy.pure
def total(values: list[i64]) -> i64:
result: i64 = 0
for value in values:
result += value
return result
@ppy.pure
def norm2(point: tuple[f64, f64, f64]) -> f64:
return point[0] * point[0] + point[1] * point[1] + point[2] * point[2]
@ppy.pure
def centroid(a: tuple[float, float], b: tuple[float, float]) -> tuple[float, float]:
return ((a[0] + b[0]) * 0.5, (a[1] + b[1]) * 0.5)
@ppy.pure
def brightness(colour: Array[int, 3]) -> int:
return colour[0] + colour[1] + colour[2]
def main() -> None:
print(dot([1.0, 2.0, 3.0], [4.0, 5.0, 6.0]))
print(total([1, 2, 3, 4, 5]))
print(total([10**30, 1]))
print(norm2((1.0, 2.0, 3.0)), centroid((0.0, 0.0), (4.0, 6.0)))
print(brightness((255, 128, 0)))
if __name__ == "__main__":
main()
Source: examples/08_native_data.