Skip to content

Something must be say about bytearray + readinto #2

@dglaude

Description

@dglaude

Based on the two recent CircuitPython meeting, it is clear that something bust be said about bytearray.

If you only have to store positive integer from 0 to 255, then that is the optimal structure to use as each entry cost one byte, where a list of int might cost 4 bytes per entry.

So if short table of short value are needed, putting them as bytearray is beneficial, making a list of tuple would be overkill as the list is an object, each tuple is an object and each int take some space.

A typical bad example is this table: https://github.com/adafruit/Adafruit_CircuitPython_IS31FL3731/blob/9993369654a47ca8763b0971e1dc847717b21cc6/adafruit_is31fl3731/keybow2040.py#L67 :

        lookup = [
            (120, 88, 104),  # 0, 0
            (136, 40, 72),  # 1, 0
            (112, 80, 96),  # 2, 0
            (128, 32, 64),  # 3, 0
            (121, 89, 105),  # 0, 1
            (137, 41, 73),  # 1, 1
            (113, 81, 97),  # 2, 1
            (129, 33, 65),  # 3, 1
            (122, 90, 106),  # 0, 2
            (138, 25, 74),  # 1, 2
            (114, 82, 98),  # 2, 2
            (130, 17, 66),  # 3, 2
            (123, 91, 107),  # 0, 3
            (139, 26, 75),  # 1, 3
            (115, 83, 99),  # 2, 3
            (131, 18, 67),  # 3, 3
        ]

All values are between 0 and 255 and with a code a little bit smarter, it can be an array of 48 bytes.

If the needed data are big, then reading them from file into a bytearray rather than to declare that in the code will make the code smaller and the memory usage more efficient. Typically a map for a game (containing reference to background tile) could be into a resource file.

One way to avoid memory activity that has a cost in fragmentation and CPU, it is best to use any function that do "readinto" an existing buffer. And that buffer should be the same everytime we have to read (like when you change level in a game). This mean a local variable in a function allocated on the stack at each call is worst than a global (to the library) variable.

Bad example are like this: https://github.com/adafruit/Adafruit_CircuitPython_MLX90640/blob/2668229b7b72dcfc2317c1e607be8ecb1c505cd9/adafruit_mlx90640.py#L123

Every time this function is called, for every "image" a new temporary array of 834 element is created:

    def getFrame(self, framebuf):
        """Request both 'halves' of a frame from the sensor, merge them
        and calculate the temperature in C for each of 32x24 pixels. Placed
        into the 768-element array passed in!"""
        emissivity = 0.95
        tr = 23.15
        mlx90640Frame = [0] * 834

Since you only make one call at a time to getFrame, it can be a more global and always the same buffer.

Maybe blurring the distinction between memory on the stack and memory in the heap is not great... and I don't exactly know how CP work inside and what is stored where, so this can give general idea on what to do or not to do.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions