diff --git a/Improving-performance-with-Viper-code.md b/Improving-performance-with-Viper-code.md index 9c1461c..24c8883 100644 --- a/Improving-performance-with-Viper-code.md +++ b/Improving-performance-with-Viper-code.md @@ -62,7 +62,7 @@ Advantages of the @micropython.native decorator: no change to the code is needed Advantage of the @micropython.viper decorator: the result can be faster, especially if integer and array operations are involved. But it is necessary to change the code. -# The viper data types: int, uint, ptr32, ptr16 and ptr8 +# The viper data types: int, uint, bool, ptr32, ptr16 and ptr8 These data types are very fast. They are not implemented as an MicroPython object but as a raw variable. They can be only used within a viper decorated function. @@ -94,6 +94,7 @@ In case you are familiar with C: The viper data types are similar to some C lang |----------------|----------------------|------| |`int` |`long int` | 32 bit signed integer| |`uint` | `unsigned long int`| 32 bit unsigned integer | +|`bool` | `long int`| 32 bit signed integer, but only 0 (False) and not zero (True) is used | |`ptr32` | `*long int` | memory pointer to a 32 bit signed integer | |`ptr16` |`*unsigned short int` |memory pointer to a 16 bit unsigned integer | |`ptr8` | `*unsigned char`|memory pointer to an 8 bit unsigned integer | @@ -103,8 +104,8 @@ In case you are familiar with C: The viper data types are similar to some C lang * The viper data types only exist in a viper function * The viper data types are detected at compile time (statically, before the program starts to run) * They are not MicroPython objects but raw variables -* The associated functions `int()`, `uint()`, `ptr8()`, `ptr16()` and `ptr32()` are type casts (similar to C language) -* The MicroPython `int` object we all know is different from the viper `int` inside a viper function. If needed, the MicroPython `int` can still be accessed as `builtins.int` (`import builtins` first) +* The associated functions `int()`, `uint()`, `bool()` `ptr8()`, `ptr16()` and `ptr32()` are type casts (similar to C language) +* The MicroPython `int` object we all know is different from the viper `int` inside a viper function. If needed, the MicroPython `int` can still be accessed as `builtins.int` (`import builtins` first). Same with `bool`. * Operations are very fast ## The viper int data type @@ -133,7 +134,7 @@ As it is usual in Python, a viper variable is of type viper `int when you assig ``` If the variable is created by assigning an expression, the viper code emitter will evaluate the expression at compile time. -Be aware: Integer expressions outside what is called the "small integer" range of MicroPython are not viper `int` but `builtins.int`. On most architectures a MicroPython small integer falls is -2\*\*29 and 2\*\*29-1. +Be aware: Integer expressions outside of what is called the "small integer" range of MicroPython are not viper `int` but `builtins.int`. On most architectures a MicroPython small integer falls is -2\*\*29 and 2\*\*29-1. For example: ```py @@ -234,7 +235,7 @@ A viper ```int``` is not an object, and thus does not support methods such as `` The \*\* operator (exponentiation, `__pow__`) is not implemented for viper ```int```. -In versions MicroPython 1.22 and prior, unary minus is not implemented, instead of `x=-a` use `x=0-a`. In version 1.23 the unary minus is being implemented, but not completely yet. +Be aware: In versions MicroPython 1.22 and prior, unary minus is not implemented, instead of `x=-a` use `x=0-a`. In version 1.23 the unary minus is being implemented, but not completely yet, so until further confirmation it's best to avoid unary minus, and use subtraction from zero instead. Be aware: Do not use shift left or right with a negative value, i.e. `x<<(-1)` or `x>>(-1)` should not be used because the result is undefined. This mirrors the C language definition for shifting. Unlike regular MicroPython, there is no check (no exception raised) for negative shift amounts. @@ -279,6 +280,38 @@ def test_uint_int_assignments(): print(f"{y=} int(y)={y=:08x}, expected 0xffffffff") ``` +## The viper bool data type +A bool viper variable can be True or False. Boolean expressions are evaluated using the viper bool data type. This makes logic computations fast. + +You create a bool viper variable by assigning `True`, `False` or the result of a logical expression of constants and viper bool variables that yields `True` or `False`, for example: +```py +@micropython.viper +def function_with_bools(): + a = True + y = 1 + z = 2 + b = y < z + c = bool(y) + # Now a, b and c are viper bool variables + # and a, b, and c are True +``` + + +You can convert a `int` or `uint` to bool using the `bool()` cast function. A zero value is interpreted as False, any value different from zero is interpreted as True. + +Similar to `builtins.int` for the viper `int` data type, `builtins.bool` can be used to have the MicroPython boolean data type available. + +As a note of minor interest, the bool viper variable is stored as a 32 bit integer: +```py +@micropython.viper +def cast_bools(): + x = int(12345) + b = bool(x) + y = int(b) + # Now y holds the same value as x +``` +Similar to `int()` and `uint()`, `bool()` is a cast operator. If used on a viper variable, only the type is changed, no conversion takes place (similar to casting in C language). + ## The viper ptr32, ptr16 and ptr8 data types These data types are pointers to memory, similar to a C language `long *p;` or `unsigned char *p`. This is rather unusual for Python, where no pointers exist and memory access is well hidden within objects that protect that access. @@ -289,10 +322,11 @@ You can assign to x[n], modifying the memory contents. There is no bounds checki ### Declaration of pointer variables with type hints on function argument ```py @micropython.viper -def myfunction( x:ptr32 )->int: +def myfunction( x:ptr32, b:bool )->int: print(x[0], x[1], x[2] ) # will print 1, 2, 3, 4 return x[1] myfunction( array.array("l", (1,2,3,4))) + ``` ## Declaration of pointer variables with ptr32(), ptr16() and ptr8() @@ -394,11 +428,18 @@ The call overhead for a viper function is substantially lower than call overhead For integer parameters, use the `int` or `uint` type hint to get automatic conversion to a viper int. The conversion is done internally by MicroPython using the `int()` or `uint()` cast operator respectively: ```py @micropython.viper -def my_function( x:int, b:uint ): - # now x and b are viper data type variables +def my_function( x:int, z:uint ): + # now x and z are viper data type variables .... ``` +Similarly, a boolean parameter is cast automatically to a viper bool: +```py +@micropython.viper +def my_function( b:bool ): + # b is a viper bool variable + .... +``` For arrays and bytearrays, use the ptr32, ptr16 and ptr8 type hints in the function parameters to get fast access to the arrays. The cast from an array to a pointer is done automatically while processing the call, i.e. a ptr8(), ptr16() or ptr32() cast is applied automatically to the argument. ```py @@ -453,7 +494,7 @@ def fun2( mypointer:ptr8 ): A side effect of this behavior is that `type(viper_variable)` always returns class `builtins.int`, because the viper variable is converted to a `builtins.int` during the call process. -Talking about detecting type: inside a viper function, `isinstance(viper_variable,int)` will give a compile-time error `NotImplementedError: conversion to object`, since `int` is a viper data type, not a MicroPython class. However, `isinstance(viper_variable, builtins.int)` will return `True` since the `viper_variable` will be converted to a MicroPython `builtins.int` automatically during the call process. +Talking about detecting type: inside a viper function, `isinstance(viper_variable,int)` will give a compile-time error `NotImplementedError: conversion to object`, since `int` is a viper data type, not a MicroPython class. However, `isinstance(viper_variable, builtins.int)` will return `True` since the `viper_variable` will be converted to a MicroPython `builtins.int` automatically during the call process. This also applies to `bool`. ## Viper function return values @@ -461,10 +502,13 @@ If the function returns a viper variable, a return type hint must be supplied, f ```py @micropython.viper -function returns_integer(param1:int)->int: +def function_returns_integer(param1:int)->int: return 1 +@micropython.viper +def function_returns_bool(x:int)->bool: + return True ``` -The conversion of the return value back to `builtins.int` is done automatically. +The conversion of the return value back to `builtins.bool` is done automatically. You can return a pointer in a viper function, but you must add the return type hint as ->ptr8, ->ptr16 or ->ptr32. The pointer returned is converted to a `builtins.int` and it's value will be the memory address of the array. The addresses are always byte addresses. The function that uses that returned integer must cast it to a pointer of the correct type to make things work, for example: ```py @@ -539,6 +583,7 @@ You can assign a viper integer to a global variable, it gets converted to a `bui The global variable `x` is of type `builtins.int` and you cannot mix viper `int` with `builtins.int`. In the example, `10` is a viper `int` constant and has to be converted to a `builtins.int` before operating. +For viper bool variables, similar rules apply. ## Example of nonlocal and closure with viper functions @@ -607,10 +652,11 @@ This is a workaround: `x[builtins.int(0):builtins.int(2)]` ## async and generators -Viper decorated functions cannot have the async attribute (it crashes) nor be generators (`NotImplementedError: native yield` compile time error) +Viper decorated functions cannot have the async attribute (it crashes) nor be generators (`NotImplementedError: native yield` compile time error`) Workaround: async functions and generators can call viper functions. +However, a viper function can call a generator. ## Type hints in the body of a viper function @@ -624,7 +670,7 @@ def myfunction(): step:int = 0 ``` -You can't use `builtins.int` as type hint, and there is no `type` statement in MicroPython. So `builtins.int` will be always without type hint. +You can't use `builtins.int` as type hint, and there is no `type` statement in MicroPython. So `builtins.int` will be always written without type hint. ## Test if a variable is of type viper int In compile time: